From 69f943f6fe46d789ebc8b8f88e6ab0fbc0732fbc Mon Sep 17 00:00:00 2001 From: Kris Baranek <20225789+krbar@users.noreply.github.com> Date: Sun, 8 Sep 2024 08:37:19 +0200 Subject: [PATCH 01/57] fix: Remove '(Preview)' label from role name - batch 3 (#3225) ## Description Remove '(Preview)' label from all 'Role Based Access Control Administrator' role as described in #3112 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.network.application-gateway](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway.yml) | | [![avm.res.network.application-security-group](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.application-security-group.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.application-security-group.yml) | | [![avm.res.network.azure-firewall](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.azure-firewall.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.azure-firewall.yml) | | [![avm.res.network.bastion-host](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.bastion-host.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.bastion-host.yml) | | [![avm.res.network.ddos-protection-plan](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.ddos-protection-plan.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.ddos-protection-plan.yml) | | [![avm.res.network.dns-forwarding-ruleset](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.dns-forwarding-ruleset.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.dns-forwarding-ruleset.yml) | | [![avm.res.network.dns-resolver](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.dns-resolver.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.dns-resolver.yml) | | [![avm.res.network.dns-zone](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.dns-zone.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.dns-zone.yml) | | [![avm.res.network.express-route-circuit](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.express-route-circuit.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.express-route-circuit.yml) | | [![avm.res.network.express-route-gateway](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.express-route-gateway.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.express-route-gateway.yml) | | [![avm.res.network.front-door-web-application-firewall-policy](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.front-door-web-application-firewall-policy.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.front-door-web-application-firewall-policy.yml) | | [![avm.res.network.front-door](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.front-door.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.front-door.yml) | | [![avm.res.network.load-balancer](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.load-balancer.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.load-balancer.yml) | | [![avm.res.network.local-network-gateway](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.local-network-gateway.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.local-network-gateway.yml) | | [![avm.res.network.nat-gateway](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.nat-gateway.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.nat-gateway.yml) | | [![avm.res.network.network-interface](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.network-interface.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.network-interface.yml) | | [![avm.res.network.network-security-group](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.network-security-group.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.network-security-group.yml) | | [![avm.res.network.network-watcher](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.network-watcher.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.network-watcher.yml) | | [![avm.res.network.private-dns-zone](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.private-dns-zone.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.private-dns-zone.yml) | | [![avm.res.network.private-endpoint](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.private-endpoint.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.private-endpoint.yml) | | [![avm.res.network.public-ip-address](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-address.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-address.yml) | | [![avm.res.network.public-ip-prefix](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-prefix.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-prefix.yml) | | [![avm.res.network.route-table](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.route-table.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.route-table.yml) | | [![avm.res.network.service-endpoint-policy](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.service-endpoint-policy.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.service-endpoint-policy.yml) | | [![avm.res.network.virtual-network-gateway](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network-gateway.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network-gateway.yml) | | [![avm.res.network.virtual-network](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network.yml) | | [![avm.res.network.virtual-wan](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.virtual-wan.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.virtual-wan.yml) | | [![avm.res.network.vpn-site](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.vpn-site.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename3&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.vpn-site.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [x] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../network/application-gateway/main.bicep | 2 +- avm/res/network/application-gateway/main.json | 4 +- .../network/application-gateway/version.json | 2 +- .../application-security-group/main.bicep | 2 +- .../application-security-group/main.json | 4 +- .../application-security-group/version.json | 4 +- avm/res/network/azure-firewall/main.bicep | 2 +- avm/res/network/azure-firewall/main.json | 4 +- avm/res/network/azure-firewall/version.json | 2 +- avm/res/network/bastion-host/main.bicep | 2 +- avm/res/network/bastion-host/main.json | 4 +- avm/res/network/bastion-host/version.json | 2 +- .../network/ddos-protection-plan/main.bicep | 2 +- .../network/ddos-protection-plan/main.json | 4 +- .../network/ddos-protection-plan/version.json | 2 +- .../network/dns-forwarding-ruleset/main.bicep | 2 +- .../network/dns-forwarding-ruleset/main.json | 4 +- .../dns-forwarding-ruleset/version.json | 2 +- avm/res/network/dns-resolver/main.bicep | 2 +- avm/res/network/dns-resolver/main.json | 4 +- avm/res/network/dns-resolver/version.json | 2 +- avm/res/network/dns-zone/a/main.bicep | 2 +- avm/res/network/dns-zone/a/main.json | 4 +- avm/res/network/dns-zone/aaaa/main.bicep | 2 +- avm/res/network/dns-zone/aaaa/main.json | 4 +- avm/res/network/dns-zone/caa/main.bicep | 2 +- avm/res/network/dns-zone/caa/main.json | 4 +- avm/res/network/dns-zone/cname/main.bicep | 2 +- avm/res/network/dns-zone/cname/main.json | 4 +- avm/res/network/dns-zone/main.bicep | 2 +- avm/res/network/dns-zone/main.json | 44 +++++++++---------- avm/res/network/dns-zone/mx/main.bicep | 2 +- avm/res/network/dns-zone/mx/main.json | 4 +- avm/res/network/dns-zone/ns/main.bicep | 2 +- avm/res/network/dns-zone/ns/main.json | 4 +- avm/res/network/dns-zone/ptr/main.bicep | 2 +- avm/res/network/dns-zone/ptr/main.json | 4 +- avm/res/network/dns-zone/soa/main.bicep | 2 +- avm/res/network/dns-zone/soa/main.json | 4 +- avm/res/network/dns-zone/srv/main.bicep | 2 +- avm/res/network/dns-zone/srv/main.json | 4 +- avm/res/network/dns-zone/txt/main.bicep | 2 +- avm/res/network/dns-zone/txt/main.json | 4 +- avm/res/network/dns-zone/version.json | 2 +- .../network/express-route-circuit/main.bicep | 2 +- .../network/express-route-circuit/main.json | 4 +- .../express-route-circuit/version.json | 2 +- .../network/express-route-gateway/main.bicep | 2 +- .../network/express-route-gateway/main.json | 4 +- .../express-route-gateway/version.json | 2 +- .../main.bicep | 2 +- .../main.json | 4 +- .../version.json | 2 +- avm/res/network/front-door/main.bicep | 2 +- avm/res/network/front-door/main.json | 4 +- avm/res/network/front-door/version.json | 2 +- avm/res/network/load-balancer/main.bicep | 2 +- avm/res/network/load-balancer/main.json | 4 +- avm/res/network/load-balancer/version.json | 2 +- .../network/local-network-gateway/main.bicep | 2 +- .../network/local-network-gateway/main.json | 4 +- .../local-network-gateway/version.json | 2 +- avm/res/network/nat-gateway/main.bicep | 2 +- avm/res/network/nat-gateway/main.json | 6 +-- avm/res/network/nat-gateway/version.json | 2 +- avm/res/network/network-interface/main.bicep | 2 +- avm/res/network/network-interface/main.json | 4 +- .../network/network-interface/version.json | 2 +- .../network/network-security-group/main.bicep | 2 +- .../network/network-security-group/main.json | 4 +- .../network-security-group/version.json | 2 +- avm/res/network/network-watcher/main.bicep | 2 +- avm/res/network/network-watcher/main.json | 4 +- avm/res/network/network-watcher/version.json | 2 +- avm/res/network/private-dns-zone/a/main.bicep | 2 +- avm/res/network/private-dns-zone/a/main.json | 4 +- .../network/private-dns-zone/aaaa/main.bicep | 2 +- .../network/private-dns-zone/aaaa/main.json | 4 +- .../network/private-dns-zone/cname/main.bicep | 2 +- .../network/private-dns-zone/cname/main.json | 4 +- avm/res/network/private-dns-zone/main.bicep | 2 +- avm/res/network/private-dns-zone/main.json | 36 +++++++-------- .../network/private-dns-zone/mx/main.bicep | 2 +- avm/res/network/private-dns-zone/mx/main.json | 4 +- .../network/private-dns-zone/ptr/main.bicep | 2 +- .../network/private-dns-zone/ptr/main.json | 4 +- .../network/private-dns-zone/soa/main.bicep | 2 +- .../network/private-dns-zone/soa/main.json | 4 +- .../network/private-dns-zone/srv/main.bicep | 2 +- .../network/private-dns-zone/srv/main.json | 4 +- .../network/private-dns-zone/txt/main.bicep | 2 +- .../network/private-dns-zone/txt/main.json | 4 +- avm/res/network/private-dns-zone/version.json | 2 +- avm/res/network/private-endpoint/main.bicep | 2 +- avm/res/network/private-endpoint/main.json | 4 +- avm/res/network/private-endpoint/version.json | 4 +- avm/res/network/public-ip-address/main.bicep | 2 +- avm/res/network/public-ip-address/main.json | 4 +- .../network/public-ip-address/version.json | 2 +- avm/res/network/public-ip-prefix/main.bicep | 2 +- avm/res/network/public-ip-prefix/main.json | 4 +- avm/res/network/public-ip-prefix/version.json | 2 +- avm/res/network/route-table/main.bicep | 2 +- avm/res/network/route-table/main.json | 4 +- avm/res/network/route-table/version.json | 2 +- .../service-endpoint-policy/main.bicep | 2 +- .../network/service-endpoint-policy/main.json | 4 +- .../service-endpoint-policy/version.json | 2 +- .../virtual-network-gateway/main.bicep | 2 +- .../network/virtual-network-gateway/main.json | 4 +- .../virtual-network-gateway/version.json | 2 +- avm/res/network/virtual-network/main.bicep | 2 +- avm/res/network/virtual-network/main.json | 8 ++-- .../network/virtual-network/subnet/main.bicep | 2 +- .../network/virtual-network/subnet/main.json | 4 +- avm/res/network/virtual-network/version.json | 2 +- avm/res/network/virtual-wan/main.bicep | 2 +- avm/res/network/virtual-wan/main.json | 4 +- avm/res/network/virtual-wan/version.json | 2 +- avm/res/network/vpn-site/main.bicep | 2 +- avm/res/network/vpn-site/main.json | 4 +- avm/res/network/vpn-site/version.json | 2 +- 122 files changed, 210 insertions(+), 210 deletions(-) diff --git a/avm/res/network/application-gateway/main.bicep b/avm/res/network/application-gateway/main.bicep index 636fa01efb..ee99759960 100644 --- a/avm/res/network/application-gateway/main.bicep +++ b/avm/res/network/application-gateway/main.bicep @@ -225,7 +225,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/application-gateway/main.json b/avm/res/network/application-gateway/main.json index 7876f434e6..9d6559c609 100644 --- a/avm/res/network/application-gateway/main.json +++ b/avm/res/network/application-gateway/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "10337247586212656300" + "templateHash": "17187265964548508241" }, "name": "Network Application Gateways", "description": "This module deploys a Network Application Gateway.", @@ -866,7 +866,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/application-gateway/version.json b/avm/res/network/application-gateway/version.json index 3f863a2bec..a8eda31021 100644 --- a/avm/res/network/application-gateway/version.json +++ b/avm/res/network/application-gateway/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/application-security-group/main.bicep b/avm/res/network/application-security-group/main.bicep index d5e5d98046..30ca43389d 100644 --- a/avm/res/network/application-security-group/main.bicep +++ b/avm/res/network/application-security-group/main.bicep @@ -24,7 +24,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/application-security-group/main.json b/avm/res/network/application-security-group/main.json index 1eeeb5c6af..9defa21100 100644 --- a/avm/res/network/application-security-group/main.json +++ b/avm/res/network/application-security-group/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16774487248704034776" + "templateHash": "12558218953270061574" }, "name": "Application Security Groups (ASG)", "description": "This module deploys an Application Security Group (ASG).", @@ -165,7 +165,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/application-security-group/version.json b/avm/res/network/application-security-group/version.json index 7fa401bdf7..1c035df49f 100644 --- a/avm/res/network/application-security-group/version.json +++ b/avm/res/network/application-security-group/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/network/azure-firewall/main.bicep b/avm/res/network/azure-firewall/main.bicep index 50709a437f..dc6a94ce87 100644 --- a/avm/res/network/azure-firewall/main.bicep +++ b/avm/res/network/azure-firewall/main.bicep @@ -153,7 +153,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/azure-firewall/main.json b/avm/res/network/azure-firewall/main.json index b86274a64e..75a9bfba6a 100644 --- a/avm/res/network/azure-firewall/main.json +++ b/avm/res/network/azure-firewall/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4172708261226601418" + "templateHash": "1087268001408720205" }, "name": "Azure Firewalls", "description": "This module deploys an Azure Firewall.", @@ -856,7 +856,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/azure-firewall/version.json b/avm/res/network/azure-firewall/version.json index 3f863a2bec..a8eda31021 100644 --- a/avm/res/network/azure-firewall/version.json +++ b/avm/res/network/azure-firewall/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/bastion-host/main.bicep b/avm/res/network/bastion-host/main.bicep index 75b7fbe9a1..3acc37b8df 100644 --- a/avm/res/network/bastion-host/main.bicep +++ b/avm/res/network/bastion-host/main.bicep @@ -90,7 +90,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/bastion-host/main.json b/avm/res/network/bastion-host/main.json index 75a5719bf3..2634dae54b 100644 --- a/avm/res/network/bastion-host/main.json +++ b/avm/res/network/bastion-host/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11950325802419024259" + "templateHash": "17557675644366875046" }, "name": "Bastion Hosts", "description": "This module deploys a Bastion Host.", @@ -341,7 +341,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/bastion-host/version.json b/avm/res/network/bastion-host/version.json index c177b1bb58..3f863a2bec 100644 --- a/avm/res/network/bastion-host/version.json +++ b/avm/res/network/bastion-host/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/ddos-protection-plan/main.bicep b/avm/res/network/ddos-protection-plan/main.bicep index 915d9d5ad5..867b4e963b 100644 --- a/avm/res/network/ddos-protection-plan/main.bicep +++ b/avm/res/network/ddos-protection-plan/main.bicep @@ -25,7 +25,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/ddos-protection-plan/main.json b/avm/res/network/ddos-protection-plan/main.json index 622b0b8632..03066b8ebf 100644 --- a/avm/res/network/ddos-protection-plan/main.json +++ b/avm/res/network/ddos-protection-plan/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "9448953054880328206" + "templateHash": "15571786459104289484" }, "name": "DDoS Protection Plans", "description": "This module deploys a DDoS Protection Plan.", @@ -166,7 +166,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/ddos-protection-plan/version.json b/avm/res/network/ddos-protection-plan/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/network/ddos-protection-plan/version.json +++ b/avm/res/network/ddos-protection-plan/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/dns-forwarding-ruleset/main.bicep b/avm/res/network/dns-forwarding-ruleset/main.bicep index 365e81861c..b811a1ff3f 100644 --- a/avm/res/network/dns-forwarding-ruleset/main.bicep +++ b/avm/res/network/dns-forwarding-ruleset/main.bicep @@ -50,7 +50,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/dns-forwarding-ruleset/main.json b/avm/res/network/dns-forwarding-ruleset/main.json index be9b89f6d8..2524162174 100644 --- a/avm/res/network/dns-forwarding-ruleset/main.json +++ b/avm/res/network/dns-forwarding-ruleset/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16210853783153815053" + "templateHash": "8260746772295919880" }, "name": "Dns Forwarding Rulesets", "description": "This template deploys an dns forwarding ruleset.", @@ -276,7 +276,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, "resources": { diff --git a/avm/res/network/dns-forwarding-ruleset/version.json b/avm/res/network/dns-forwarding-ruleset/version.json index 76049e1c4a..13669e6601 100644 --- a/avm/res/network/dns-forwarding-ruleset/version.json +++ b/avm/res/network/dns-forwarding-ruleset/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/dns-resolver/main.bicep b/avm/res/network/dns-resolver/main.bicep index e538a5975f..09a7f68b39 100644 --- a/avm/res/network/dns-resolver/main.bicep +++ b/avm/res/network/dns-resolver/main.bicep @@ -58,7 +58,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/dns-resolver/main.json b/avm/res/network/dns-resolver/main.json index 3922380b24..9cf99b4b2f 100644 --- a/avm/res/network/dns-resolver/main.json +++ b/avm/res/network/dns-resolver/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "8714442114797304062" + "templateHash": "4241820354615246390" }, "name": "DNS Resolver", "description": "This module deploys a DNS Resolver.", @@ -278,7 +278,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, "resources": { diff --git a/avm/res/network/dns-resolver/version.json b/avm/res/network/dns-resolver/version.json index 13669e6601..ea4f3b6e67 100644 --- a/avm/res/network/dns-resolver/version.json +++ b/avm/res/network/dns-resolver/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/dns-zone/a/main.bicep b/avm/res/network/dns-zone/a/main.bicep index 8f9d60922f..b682a5f2cc 100644 --- a/avm/res/network/dns-zone/a/main.bicep +++ b/avm/res/network/dns-zone/a/main.bicep @@ -51,7 +51,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/dns-zone/a/main.json b/avm/res/network/dns-zone/a/main.json index df0f6b644a..e6f51bbffa 100644 --- a/avm/res/network/dns-zone/a/main.json +++ b/avm/res/network/dns-zone/a/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "757998912186666805" + "templateHash": "17184746855591114267" }, "name": "Public DNS Zone A record", "description": "This module deploys a Public DNS Zone A record.", @@ -153,7 +153,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/dns-zone/aaaa/main.bicep b/avm/res/network/dns-zone/aaaa/main.bicep index 0c08a1f834..2bd383685d 100644 --- a/avm/res/network/dns-zone/aaaa/main.bicep +++ b/avm/res/network/dns-zone/aaaa/main.bicep @@ -51,7 +51,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/dns-zone/aaaa/main.json b/avm/res/network/dns-zone/aaaa/main.json index 7192d58558..052ad33a1c 100644 --- a/avm/res/network/dns-zone/aaaa/main.json +++ b/avm/res/network/dns-zone/aaaa/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "7014742305851451531" + "templateHash": "7817764105789679714" }, "name": "Public DNS Zone AAAA record", "description": "This module deploys a Public DNS Zone AAAA record.", @@ -153,7 +153,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/dns-zone/caa/main.bicep b/avm/res/network/dns-zone/caa/main.bicep index 4e05e82730..d9df93dca5 100644 --- a/avm/res/network/dns-zone/caa/main.bicep +++ b/avm/res/network/dns-zone/caa/main.bicep @@ -48,7 +48,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/dns-zone/caa/main.json b/avm/res/network/dns-zone/caa/main.json index ee588f2740..b384c36531 100644 --- a/avm/res/network/dns-zone/caa/main.json +++ b/avm/res/network/dns-zone/caa/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "15336680989597404697" + "templateHash": "7392290278324947651" }, "name": "Public DNS Zone CAA record", "description": "This module deploys a Public DNS Zone CAA record.", @@ -146,7 +146,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/dns-zone/cname/main.bicep b/avm/res/network/dns-zone/cname/main.bicep index b5373b54f6..78c463ae6c 100644 --- a/avm/res/network/dns-zone/cname/main.bicep +++ b/avm/res/network/dns-zone/cname/main.bicep @@ -51,7 +51,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/dns-zone/cname/main.json b/avm/res/network/dns-zone/cname/main.json index 8025a23f35..5d88e2d542 100644 --- a/avm/res/network/dns-zone/cname/main.json +++ b/avm/res/network/dns-zone/cname/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "13074182414562688422" + "templateHash": "14841477015461182740" }, "name": "Public DNS Zone CNAME record", "description": "This module deploys a Public DNS Zone CNAME record.", @@ -153,7 +153,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/dns-zone/main.bicep b/avm/res/network/dns-zone/main.bicep index 85c81b2d53..727a297599 100644 --- a/avm/res/network/dns-zone/main.bicep +++ b/avm/res/network/dns-zone/main.bicep @@ -80,7 +80,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/dns-zone/main.json b/avm/res/network/dns-zone/main.json index fbda4b028e..f11e90583f 100644 --- a/avm/res/network/dns-zone/main.json +++ b/avm/res/network/dns-zone/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "2883962289867556365" + "templateHash": "3210797771365278499" }, "name": "Public DNS Zones", "description": "This module deploys a Public DNS zone.", @@ -855,7 +855,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -970,7 +970,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "757998912186666805" + "templateHash": "17184746855591114267" }, "name": "Public DNS Zone A record", "description": "This module deploys a Public DNS Zone A record.", @@ -1117,7 +1117,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -1238,7 +1238,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "7014742305851451531" + "templateHash": "7817764105789679714" }, "name": "Public DNS Zone AAAA record", "description": "This module deploys a Public DNS Zone AAAA record.", @@ -1385,7 +1385,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -1506,7 +1506,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "13074182414562688422" + "templateHash": "14841477015461182740" }, "name": "Public DNS Zone CNAME record", "description": "This module deploys a Public DNS Zone CNAME record.", @@ -1653,7 +1653,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -1771,7 +1771,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "15336680989597404697" + "templateHash": "7392290278324947651" }, "name": "Public DNS Zone CAA record", "description": "This module deploys a Public DNS Zone CAA record.", @@ -1911,7 +1911,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -2028,7 +2028,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "5108823161751025831" + "templateHash": "6270819353969968995" }, "name": "Public DNS Zone MX record", "description": "This module deploys a Public DNS Zone MX record.", @@ -2168,7 +2168,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -2285,7 +2285,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11663723082194219520" + "templateHash": "14380397644512533588" }, "name": "Public DNS Zone NS record", "description": "This module deploys a Public DNS Zone NS record.", @@ -2425,7 +2425,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -2542,7 +2542,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "9140925940202553676" + "templateHash": "832235689182152716" }, "name": "Public DNS Zone PTR record", "description": "This module deploys a Public DNS Zone PTR record.", @@ -2682,7 +2682,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -2799,7 +2799,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "14564615037136781676" + "templateHash": "18205185605727276078" }, "name": "Public DNS Zone SOA record", "description": "This module deploys a Public DNS Zone SOA record.", @@ -2939,7 +2939,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -3056,7 +3056,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4581413191057367399" + "templateHash": "17838962507786847676" }, "name": "Public DNS Zone SRV record", "description": "This module deploys a Public DNS Zone SRV record.", @@ -3196,7 +3196,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -3313,7 +3313,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "5111536727799165587" + "templateHash": "8037353991621891503" }, "name": "Public DNS Zone TXT record", "description": "This module deploys a Public DNS Zone TXT record.", @@ -3453,7 +3453,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/dns-zone/mx/main.bicep b/avm/res/network/dns-zone/mx/main.bicep index 48d4e48c1f..03b4b9319f 100644 --- a/avm/res/network/dns-zone/mx/main.bicep +++ b/avm/res/network/dns-zone/mx/main.bicep @@ -48,7 +48,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/dns-zone/mx/main.json b/avm/res/network/dns-zone/mx/main.json index da370db49a..8e631bdce2 100644 --- a/avm/res/network/dns-zone/mx/main.json +++ b/avm/res/network/dns-zone/mx/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "5108823161751025831" + "templateHash": "6270819353969968995" }, "name": "Public DNS Zone MX record", "description": "This module deploys a Public DNS Zone MX record.", @@ -146,7 +146,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/dns-zone/ns/main.bicep b/avm/res/network/dns-zone/ns/main.bicep index 2f1839197f..1203a397fd 100644 --- a/avm/res/network/dns-zone/ns/main.bicep +++ b/avm/res/network/dns-zone/ns/main.bicep @@ -48,7 +48,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/dns-zone/ns/main.json b/avm/res/network/dns-zone/ns/main.json index 94c90714f8..06fd77734a 100644 --- a/avm/res/network/dns-zone/ns/main.json +++ b/avm/res/network/dns-zone/ns/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11663723082194219520" + "templateHash": "14380397644512533588" }, "name": "Public DNS Zone NS record", "description": "This module deploys a Public DNS Zone NS record.", @@ -146,7 +146,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/dns-zone/ptr/main.bicep b/avm/res/network/dns-zone/ptr/main.bicep index cb82fbcf1f..a326596289 100644 --- a/avm/res/network/dns-zone/ptr/main.bicep +++ b/avm/res/network/dns-zone/ptr/main.bicep @@ -48,7 +48,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/dns-zone/ptr/main.json b/avm/res/network/dns-zone/ptr/main.json index 34bbd57230..9e982b7373 100644 --- a/avm/res/network/dns-zone/ptr/main.json +++ b/avm/res/network/dns-zone/ptr/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "9140925940202553676" + "templateHash": "832235689182152716" }, "name": "Public DNS Zone PTR record", "description": "This module deploys a Public DNS Zone PTR record.", @@ -146,7 +146,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/dns-zone/soa/main.bicep b/avm/res/network/dns-zone/soa/main.bicep index 8596cbb21b..4e708fc5ff 100644 --- a/avm/res/network/dns-zone/soa/main.bicep +++ b/avm/res/network/dns-zone/soa/main.bicep @@ -48,7 +48,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/dns-zone/soa/main.json b/avm/res/network/dns-zone/soa/main.json index e106c03424..59d9439bb1 100644 --- a/avm/res/network/dns-zone/soa/main.json +++ b/avm/res/network/dns-zone/soa/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "14564615037136781676" + "templateHash": "18205185605727276078" }, "name": "Public DNS Zone SOA record", "description": "This module deploys a Public DNS Zone SOA record.", @@ -146,7 +146,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/dns-zone/srv/main.bicep b/avm/res/network/dns-zone/srv/main.bicep index 52c9ac62e2..9dc88b3545 100644 --- a/avm/res/network/dns-zone/srv/main.bicep +++ b/avm/res/network/dns-zone/srv/main.bicep @@ -48,7 +48,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/dns-zone/srv/main.json b/avm/res/network/dns-zone/srv/main.json index e8e21b9958..4f8506f0e7 100644 --- a/avm/res/network/dns-zone/srv/main.json +++ b/avm/res/network/dns-zone/srv/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4581413191057367399" + "templateHash": "17838962507786847676" }, "name": "Public DNS Zone SRV record", "description": "This module deploys a Public DNS Zone SRV record.", @@ -146,7 +146,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/dns-zone/txt/main.bicep b/avm/res/network/dns-zone/txt/main.bicep index 4be83d7ce6..2877ed1362 100644 --- a/avm/res/network/dns-zone/txt/main.bicep +++ b/avm/res/network/dns-zone/txt/main.bicep @@ -48,7 +48,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/dns-zone/txt/main.json b/avm/res/network/dns-zone/txt/main.json index d7cd9dfab3..ef9385fe38 100644 --- a/avm/res/network/dns-zone/txt/main.json +++ b/avm/res/network/dns-zone/txt/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "5111536727799165587" + "templateHash": "8037353991621891503" }, "name": "Public DNS Zone TXT record", "description": "This module deploys a Public DNS Zone TXT record.", @@ -146,7 +146,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/dns-zone/version.json b/avm/res/network/dns-zone/version.json index 13669e6601..ea4f3b6e67 100644 --- a/avm/res/network/dns-zone/version.json +++ b/avm/res/network/dns-zone/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/express-route-circuit/main.bicep b/avm/res/network/express-route-circuit/main.bicep index 26e0599131..87db72e19b 100644 --- a/avm/res/network/express-route-circuit/main.bicep +++ b/avm/res/network/express-route-circuit/main.bicep @@ -93,7 +93,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/express-route-circuit/main.json b/avm/res/network/express-route-circuit/main.json index f89ea8c582..80d14fcfac 100644 --- a/avm/res/network/express-route-circuit/main.json +++ b/avm/res/network/express-route-circuit/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "8576129412058428008" + "templateHash": "1086876492460058037" }, "name": "ExpressRoute Circuits", "description": "This module deploys an Express Route Circuit.", @@ -414,7 +414,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/express-route-circuit/version.json b/avm/res/network/express-route-circuit/version.json index 729ac87673..76049e1c4a 100644 --- a/avm/res/network/express-route-circuit/version.json +++ b/avm/res/network/express-route-circuit/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/express-route-gateway/main.bicep b/avm/res/network/express-route-gateway/main.bicep index 3690950f07..d863523b95 100644 --- a/avm/res/network/express-route-gateway/main.bicep +++ b/avm/res/network/express-route-gateway/main.bicep @@ -43,7 +43,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/express-route-gateway/main.json b/avm/res/network/express-route-gateway/main.json index f8e8c3f584..3ec3292057 100644 --- a/avm/res/network/express-route-gateway/main.json +++ b/avm/res/network/express-route-gateway/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "3952012926909383824" + "templateHash": "15136556135111983685" }, "name": "Express Route Gateways", "description": "This module deploys an Express Route Gateway.", @@ -200,7 +200,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/express-route-gateway/version.json b/avm/res/network/express-route-gateway/version.json index e42c3d9e5f..7e1d3f4157 100644 --- a/avm/res/network/express-route-gateway/version.json +++ b/avm/res/network/express-route-gateway/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.6", + "version": "0.7", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/front-door-web-application-firewall-policy/main.bicep b/avm/res/network/front-door-web-application-firewall-policy/main.bicep index de60fe5ffc..377833654b 100644 --- a/avm/res/network/front-door-web-application-firewall-policy/main.bicep +++ b/avm/res/network/front-door-web-application-firewall-policy/main.bicep @@ -79,7 +79,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/front-door-web-application-firewall-policy/main.json b/avm/res/network/front-door-web-application-firewall-policy/main.json index 52958c1ef6..de8d1b031c 100644 --- a/avm/res/network/front-door-web-application-firewall-policy/main.json +++ b/avm/res/network/front-door-web-application-firewall-policy/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "12441964439062961983" + "templateHash": "8148563999971279398" }, "name": "Front Door Web Application Firewall (WAF) Policies", "description": "This module deploys a Front Door Web Application Firewall (WAF) Policy.", @@ -238,7 +238,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/front-door-web-application-firewall-policy/version.json b/avm/res/network/front-door-web-application-firewall-policy/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/network/front-door-web-application-firewall-policy/version.json +++ b/avm/res/network/front-door-web-application-firewall-policy/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/front-door/main.bicep b/avm/res/network/front-door/main.bicep index eef46273b3..6e47218f83 100644 --- a/avm/res/network/front-door/main.bicep +++ b/avm/res/network/front-door/main.bicep @@ -61,7 +61,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/front-door/main.json b/avm/res/network/front-door/main.json index 46ee05c293..9a41829948 100644 --- a/avm/res/network/front-door/main.json +++ b/avm/res/network/front-door/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "13725022226932755148" + "templateHash": "1188225161775362575" }, "name": "Azure Front Doors", "description": "This module deploys an Azure Front Door.", @@ -353,7 +353,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/front-door/version.json b/avm/res/network/front-door/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/network/front-door/version.json +++ b/avm/res/network/front-door/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/load-balancer/main.bicep b/avm/res/network/load-balancer/main.bicep index 4465f3843d..3d9ff8a075 100644 --- a/avm/res/network/load-balancer/main.bicep +++ b/avm/res/network/load-balancer/main.bicep @@ -197,7 +197,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/load-balancer/main.json b/avm/res/network/load-balancer/main.json index 1c0a372143..5ea80aaab2 100644 --- a/avm/res/network/load-balancer/main.json +++ b/avm/res/network/load-balancer/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16763526395876685201" + "templateHash": "3722345365989936168" }, "name": "Load Balancers", "description": "This module deploys a Load Balancer.", @@ -430,7 +430,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/load-balancer/version.json b/avm/res/network/load-balancer/version.json index 76049e1c4a..13669e6601 100644 --- a/avm/res/network/load-balancer/version.json +++ b/avm/res/network/load-balancer/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/local-network-gateway/main.bicep b/avm/res/network/local-network-gateway/main.bicep index fd1c0daf10..42dd4c60a5 100644 --- a/avm/res/network/local-network-gateway/main.bicep +++ b/avm/res/network/local-network-gateway/main.bicep @@ -53,7 +53,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/local-network-gateway/main.json b/avm/res/network/local-network-gateway/main.json index 3cbc3596ef..864e3f2d9a 100644 --- a/avm/res/network/local-network-gateway/main.json +++ b/avm/res/network/local-network-gateway/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16451984976685243961" + "templateHash": "9499978663434993488" }, "name": "Local Network Gateways", "description": "This module deploys a Local Network Gateway.", @@ -212,7 +212,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/local-network-gateway/version.json b/avm/res/network/local-network-gateway/version.json index 729ac87673..76049e1c4a 100644 --- a/avm/res/network/local-network-gateway/version.json +++ b/avm/res/network/local-network-gateway/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/nat-gateway/main.bicep b/avm/res/network/nat-gateway/main.bicep index 95bfd36cf2..8836c815fe 100644 --- a/avm/res/network/nat-gateway/main.bicep +++ b/avm/res/network/nat-gateway/main.bicep @@ -52,7 +52,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/nat-gateway/main.json b/avm/res/network/nat-gateway/main.json index 013f0a6a66..414a6e0129 100644 --- a/avm/res/network/nat-gateway/main.json +++ b/avm/res/network/nat-gateway/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "693015077235905038" + "templateHash": "3430947452943320440" }, "name": "NAT Gateways", "description": "This module deploys a NAT Gateway.", @@ -213,7 +213,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -340,7 +340,7 @@ "value": "[coalesce(tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" }, "zones": { - "value": "[tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'zones')]" + "value": "[coalesce(tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'zones'), if(not(equals(parameters('zone'), 0)), createArray(parameters('zone')), null()))]" }, "enableTelemetry": { "value": "[coalesce(tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" diff --git a/avm/res/network/nat-gateway/version.json b/avm/res/network/nat-gateway/version.json index 6134ccf0a2..2b6eff08f9 100644 --- a/avm/res/network/nat-gateway/version.json +++ b/avm/res/network/nat-gateway/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "1.1", + "version": "1.2", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/network-interface/main.bicep b/avm/res/network/network-interface/main.bicep index f708ad2ac2..18b83cb1e4 100644 --- a/avm/res/network/network-interface/main.bicep +++ b/avm/res/network/network-interface/main.bicep @@ -83,7 +83,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/network-interface/main.json b/avm/res/network/network-interface/main.json index c8ccc7adfc..7a1707f3b8 100644 --- a/avm/res/network/network-interface/main.json +++ b/avm/res/network/network-interface/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "14406936031965142886" + "templateHash": "14943826288469538068" }, "name": "Network Interface", "description": "This module deploys a Network Interface.", @@ -362,7 +362,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, "resources": { diff --git a/avm/res/network/network-interface/version.json b/avm/res/network/network-interface/version.json index 76049e1c4a..13669e6601 100644 --- a/avm/res/network/network-interface/version.json +++ b/avm/res/network/network-interface/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/network-security-group/main.bicep b/avm/res/network/network-security-group/main.bicep index ad4989e7ee..47b0389364 100644 --- a/avm/res/network/network-security-group/main.bicep +++ b/avm/res/network/network-security-group/main.bicep @@ -37,7 +37,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/network-security-group/main.json b/avm/res/network/network-security-group/main.json index 5dbc84d508..e1911e67ba 100644 --- a/avm/res/network/network-security-group/main.json +++ b/avm/res/network/network-security-group/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "3065442976821754784" + "templateHash": "637344524661932746" }, "name": "Network Security Groups", "description": "This module deploys a Network security Group (NSG).", @@ -440,7 +440,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/network-security-group/version.json b/avm/res/network/network-security-group/version.json index 3f863a2bec..a8eda31021 100644 --- a/avm/res/network/network-security-group/version.json +++ b/avm/res/network/network-security-group/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/network-watcher/main.bicep b/avm/res/network/network-watcher/main.bicep index 5a4f0922c7..08eb2a4af7 100644 --- a/avm/res/network/network-watcher/main.bicep +++ b/avm/res/network/network-watcher/main.bicep @@ -35,7 +35,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/network-watcher/main.json b/avm/res/network/network-watcher/main.json index de9c03a23f..1c40e0d2f7 100644 --- a/avm/res/network/network-watcher/main.json +++ b/avm/res/network/network-watcher/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4213182635541317851" + "templateHash": "1861651489182627832" }, "name": "Network Watchers", "description": "This module deploys a Network Watcher.", @@ -182,7 +182,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/network-watcher/version.json b/avm/res/network/network-watcher/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/network/network-watcher/version.json +++ b/avm/res/network/network-watcher/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/private-dns-zone/a/main.bicep b/avm/res/network/private-dns-zone/a/main.bicep index a45dd730e9..1fc4ee7f39 100644 --- a/avm/res/network/private-dns-zone/a/main.bicep +++ b/avm/res/network/private-dns-zone/a/main.bicep @@ -32,7 +32,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/private-dns-zone/a/main.json b/avm/res/network/private-dns-zone/a/main.json index 3e788addc2..d002e8a25b 100644 --- a/avm/res/network/private-dns-zone/a/main.json +++ b/avm/res/network/private-dns-zone/a/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11343565859504250241" + "templateHash": "1641889417618452692" }, "name": "Private DNS Zone A record", "description": "This module deploys a Private DNS Zone A record.", @@ -142,7 +142,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/private-dns-zone/aaaa/main.bicep b/avm/res/network/private-dns-zone/aaaa/main.bicep index 3ebf64ba21..9b9795edfe 100644 --- a/avm/res/network/private-dns-zone/aaaa/main.bicep +++ b/avm/res/network/private-dns-zone/aaaa/main.bicep @@ -32,7 +32,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/private-dns-zone/aaaa/main.json b/avm/res/network/private-dns-zone/aaaa/main.json index c6b95cc2f8..5524e93399 100644 --- a/avm/res/network/private-dns-zone/aaaa/main.json +++ b/avm/res/network/private-dns-zone/aaaa/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "3700934406905797316" + "templateHash": "17163414995652446126" }, "name": "Private DNS Zone AAAA record", "description": "This module deploys a Private DNS Zone AAAA record.", @@ -142,7 +142,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/private-dns-zone/cname/main.bicep b/avm/res/network/private-dns-zone/cname/main.bicep index cb2e472857..a499480795 100644 --- a/avm/res/network/private-dns-zone/cname/main.bicep +++ b/avm/res/network/private-dns-zone/cname/main.bicep @@ -32,7 +32,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/private-dns-zone/cname/main.json b/avm/res/network/private-dns-zone/cname/main.json index 70fa98a182..c88bc5edd9 100644 --- a/avm/res/network/private-dns-zone/cname/main.json +++ b/avm/res/network/private-dns-zone/cname/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "12929118683431932277" + "templateHash": "2493714129104385633" }, "name": "Private DNS Zone CNAME record", "description": "This module deploys a Private DNS Zone CNAME record.", @@ -142,7 +142,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/private-dns-zone/main.bicep b/avm/res/network/private-dns-zone/main.bicep index 2c2f71c0e5..f744328ada 100644 --- a/avm/res/network/private-dns-zone/main.bicep +++ b/avm/res/network/private-dns-zone/main.bicep @@ -59,7 +59,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/private-dns-zone/main.json b/avm/res/network/private-dns-zone/main.json index a55804ff6f..fb66c5f768 100644 --- a/avm/res/network/private-dns-zone/main.json +++ b/avm/res/network/private-dns-zone/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16905725400119435284" + "templateHash": "5152250446888543349" }, "name": "Private DNS Zones", "description": "This module deploys a Private DNS zone.", @@ -747,7 +747,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, "resources": { @@ -855,7 +855,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11343565859504250241" + "templateHash": "1641889417618452692" }, "name": "Private DNS Zone A record", "description": "This module deploys a Private DNS Zone A record.", @@ -991,7 +991,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -1108,7 +1108,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "3700934406905797316" + "templateHash": "17163414995652446126" }, "name": "Private DNS Zone AAAA record", "description": "This module deploys a Private DNS Zone AAAA record.", @@ -1244,7 +1244,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -1361,7 +1361,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "12929118683431932277" + "templateHash": "2493714129104385633" }, "name": "Private DNS Zone CNAME record", "description": "This module deploys a Private DNS Zone CNAME record.", @@ -1497,7 +1497,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -1614,7 +1614,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "14178508926823177155" + "templateHash": "10928449924272756679" }, "name": "Private DNS Zone MX record", "description": "This module deploys a Private DNS Zone MX record.", @@ -1750,7 +1750,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -1867,7 +1867,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "8082128465097964607" + "templateHash": "13191587152357386110" }, "name": "Private DNS Zone PTR record", "description": "This module deploys a Private DNS Zone PTR record.", @@ -2003,7 +2003,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -2120,7 +2120,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "6755707328778392735" + "templateHash": "12872700379964561295" }, "name": "Private DNS Zone SOA record", "description": "This module deploys a Private DNS Zone SOA record.", @@ -2256,7 +2256,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -2373,7 +2373,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "662436323695062210" + "templateHash": "12918383495773487180" }, "name": "Private DNS Zone SRV record", "description": "This module deploys a Private DNS Zone SRV record.", @@ -2509,7 +2509,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -2626,7 +2626,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "356727884506799436" + "templateHash": "128006490354221158" }, "name": "Private DNS Zone TXT record", "description": "This module deploys a Private DNS Zone TXT record.", @@ -2762,7 +2762,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/private-dns-zone/mx/main.bicep b/avm/res/network/private-dns-zone/mx/main.bicep index e9bab6df30..5a6a30d479 100644 --- a/avm/res/network/private-dns-zone/mx/main.bicep +++ b/avm/res/network/private-dns-zone/mx/main.bicep @@ -32,7 +32,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/private-dns-zone/mx/main.json b/avm/res/network/private-dns-zone/mx/main.json index 70afeb7694..05a49ba0f9 100644 --- a/avm/res/network/private-dns-zone/mx/main.json +++ b/avm/res/network/private-dns-zone/mx/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "14178508926823177155" + "templateHash": "10928449924272756679" }, "name": "Private DNS Zone MX record", "description": "This module deploys a Private DNS Zone MX record.", @@ -142,7 +142,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/private-dns-zone/ptr/main.bicep b/avm/res/network/private-dns-zone/ptr/main.bicep index f00fed8516..54bc43b33a 100644 --- a/avm/res/network/private-dns-zone/ptr/main.bicep +++ b/avm/res/network/private-dns-zone/ptr/main.bicep @@ -32,7 +32,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/private-dns-zone/ptr/main.json b/avm/res/network/private-dns-zone/ptr/main.json index a2ae83cb73..24715732db 100644 --- a/avm/res/network/private-dns-zone/ptr/main.json +++ b/avm/res/network/private-dns-zone/ptr/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "8082128465097964607" + "templateHash": "13191587152357386110" }, "name": "Private DNS Zone PTR record", "description": "This module deploys a Private DNS Zone PTR record.", @@ -142,7 +142,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/private-dns-zone/soa/main.bicep b/avm/res/network/private-dns-zone/soa/main.bicep index b5df6a6b5e..c8d6de9315 100644 --- a/avm/res/network/private-dns-zone/soa/main.bicep +++ b/avm/res/network/private-dns-zone/soa/main.bicep @@ -32,7 +32,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/private-dns-zone/soa/main.json b/avm/res/network/private-dns-zone/soa/main.json index 8f6068b730..ccbd28a1da 100644 --- a/avm/res/network/private-dns-zone/soa/main.json +++ b/avm/res/network/private-dns-zone/soa/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "6755707328778392735" + "templateHash": "12872700379964561295" }, "name": "Private DNS Zone SOA record", "description": "This module deploys a Private DNS Zone SOA record.", @@ -142,7 +142,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/private-dns-zone/srv/main.bicep b/avm/res/network/private-dns-zone/srv/main.bicep index 8d5f2c4115..bf1fa65b04 100644 --- a/avm/res/network/private-dns-zone/srv/main.bicep +++ b/avm/res/network/private-dns-zone/srv/main.bicep @@ -32,7 +32,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/private-dns-zone/srv/main.json b/avm/res/network/private-dns-zone/srv/main.json index 8a115f2fa6..4a61202acd 100644 --- a/avm/res/network/private-dns-zone/srv/main.json +++ b/avm/res/network/private-dns-zone/srv/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "662436323695062210" + "templateHash": "12918383495773487180" }, "name": "Private DNS Zone SRV record", "description": "This module deploys a Private DNS Zone SRV record.", @@ -142,7 +142,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/private-dns-zone/txt/main.bicep b/avm/res/network/private-dns-zone/txt/main.bicep index 01cdfa971d..a0cb8468f3 100644 --- a/avm/res/network/private-dns-zone/txt/main.bicep +++ b/avm/res/network/private-dns-zone/txt/main.bicep @@ -32,7 +32,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/private-dns-zone/txt/main.json b/avm/res/network/private-dns-zone/txt/main.json index da397eb782..98d9663ae8 100644 --- a/avm/res/network/private-dns-zone/txt/main.json +++ b/avm/res/network/private-dns-zone/txt/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "356727884506799436" + "templateHash": "128006490354221158" }, "name": "Private DNS Zone TXT record", "description": "This module deploys a Private DNS Zone TXT record.", @@ -142,7 +142,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/private-dns-zone/version.json b/avm/res/network/private-dns-zone/version.json index ea4f3b6e67..21226dd43f 100644 --- a/avm/res/network/private-dns-zone/version.json +++ b/avm/res/network/private-dns-zone/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/private-endpoint/main.bicep b/avm/res/network/private-endpoint/main.bicep index f30b4f59f6..8cc557c687 100644 --- a/avm/res/network/private-endpoint/main.bicep +++ b/avm/res/network/private-endpoint/main.bicep @@ -72,7 +72,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/private-endpoint/main.json b/avm/res/network/private-endpoint/main.json index f5b0f0a552..3ccb1b2a1a 100644 --- a/avm/res/network/private-endpoint/main.json +++ b/avm/res/network/private-endpoint/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "890546444303772798" + "templateHash": "2004143709646884954" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint.", @@ -420,7 +420,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, "resources": { diff --git a/avm/res/network/private-endpoint/version.json b/avm/res/network/private-endpoint/version.json index 35040975ae..0f81d22abc 100644 --- a/avm/res/network/private-endpoint/version.json +++ b/avm/res/network/private-endpoint/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.7", + "version": "0.8", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/network/public-ip-address/main.bicep b/avm/res/network/public-ip-address/main.bicep index de3e06e253..43a77d2454 100644 --- a/avm/res/network/public-ip-address/main.bicep +++ b/avm/res/network/public-ip-address/main.bicep @@ -103,7 +103,7 @@ var builtInRoleNames = { 'b12aa53e-6015-4669-85d0-8515ebb3ae7f' ) Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/public-ip-address/main.json b/avm/res/network/public-ip-address/main.json index 006fbb7955..52d712a6d2 100644 --- a/avm/res/network/public-ip-address/main.json +++ b/avm/res/network/public-ip-address/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "3941145312505752817" + "templateHash": "7554660773280925072" }, "name": "Public IP Addresses", "description": "This module deploys a Public IP Address.", @@ -455,7 +455,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" } }, "resources": { diff --git a/avm/res/network/public-ip-address/version.json b/avm/res/network/public-ip-address/version.json index a8eda31021..e42c3d9e5f 100644 --- a/avm/res/network/public-ip-address/version.json +++ b/avm/res/network/public-ip-address/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/public-ip-prefix/main.bicep b/avm/res/network/public-ip-prefix/main.bicep index 37b640a985..ce7ae8941a 100644 --- a/avm/res/network/public-ip-prefix/main.bicep +++ b/avm/res/network/public-ip-prefix/main.bicep @@ -49,7 +49,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/public-ip-prefix/main.json b/avm/res/network/public-ip-prefix/main.json index 1faf94e7fa..f3c6be1419 100644 --- a/avm/res/network/public-ip-prefix/main.json +++ b/avm/res/network/public-ip-prefix/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "14196668450539933413" + "templateHash": "2074867794511783977" }, "name": "Public IP Prefixes", "description": "This module deploys a Public IP Prefix.", @@ -201,7 +201,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/public-ip-prefix/version.json b/avm/res/network/public-ip-prefix/version.json index 3f863a2bec..a8eda31021 100644 --- a/avm/res/network/public-ip-prefix/version.json +++ b/avm/res/network/public-ip-prefix/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/route-table/main.bicep b/avm/res/network/route-table/main.bicep index b4365c4edc..65b34c577b 100644 --- a/avm/res/network/route-table/main.bicep +++ b/avm/res/network/route-table/main.bicep @@ -34,7 +34,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/route-table/main.json b/avm/res/network/route-table/main.json index 2e9fb6794d..75263e75fa 100644 --- a/avm/res/network/route-table/main.json +++ b/avm/res/network/route-table/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "17607817156422907174" + "templateHash": "1229234901077806053" }, "name": "Route Tables", "description": "This module deploys a User Defined Route Table (UDR).", @@ -236,7 +236,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/route-table/version.json b/avm/res/network/route-table/version.json index c177b1bb58..3f863a2bec 100644 --- a/avm/res/network/route-table/version.json +++ b/avm/res/network/route-table/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/service-endpoint-policy/main.bicep b/avm/res/network/service-endpoint-policy/main.bicep index e4c4530f09..cb41bff18a 100644 --- a/avm/res/network/service-endpoint-policy/main.bicep +++ b/avm/res/network/service-endpoint-policy/main.bicep @@ -39,7 +39,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/service-endpoint-policy/main.json b/avm/res/network/service-endpoint-policy/main.json index b8d6b768bd..910cdb16c3 100644 --- a/avm/res/network/service-endpoint-policy/main.json +++ b/avm/res/network/service-endpoint-policy/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "10068025317868682479" + "templateHash": "5520853908432842946" }, "name": "Service-Endpoint-Policy", "description": "This module deploys a Service Endpoint Policy.", @@ -189,7 +189,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/service-endpoint-policy/version.json b/avm/res/network/service-endpoint-policy/version.json index 729ac87673..76049e1c4a 100644 --- a/avm/res/network/service-endpoint-policy/version.json +++ b/avm/res/network/service-endpoint-policy/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/virtual-network-gateway/main.bicep b/avm/res/network/virtual-network-gateway/main.bicep index dd0d076ba8..6b73abca5d 100644 --- a/avm/res/network/virtual-network-gateway/main.bicep +++ b/avm/res/network/virtual-network-gateway/main.bicep @@ -251,7 +251,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/virtual-network-gateway/main.json b/avm/res/network/virtual-network-gateway/main.json index c270f32631..19876a6210 100644 --- a/avm/res/network/virtual-network-gateway/main.json +++ b/avm/res/network/virtual-network-gateway/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "17741491171223900170" + "templateHash": "15596993518588024968" }, "name": "Virtual Network Gateways", "description": "This module deploys a Virtual Network Gateway.", @@ -512,7 +512,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/virtual-network-gateway/version.json b/avm/res/network/virtual-network-gateway/version.json index 729ac87673..76049e1c4a 100644 --- a/avm/res/network/virtual-network-gateway/version.json +++ b/avm/res/network/virtual-network-gateway/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/virtual-network/main.bicep b/avm/res/network/virtual-network/main.bicep index 8df104e242..c5d51b21ee 100644 --- a/avm/res/network/virtual-network/main.bicep +++ b/avm/res/network/virtual-network/main.bicep @@ -66,7 +66,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/virtual-network/main.json b/avm/res/network/virtual-network/main.json index 564c4a742c..48dff6ec21 100644 --- a/avm/res/network/virtual-network/main.json +++ b/avm/res/network/virtual-network/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "5706783765271345287" + "templateHash": "17645744687566805489" }, "name": "Virtual Networks", "description": "This module deploys a Virtual Network (vNet).", @@ -611,7 +611,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -805,7 +805,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "9295856151641467142" + "templateHash": "5699372618313647761" }, "name": "Virtual Network Subnets", "description": "This module deploys a Virtual Network Subnet.", @@ -1030,7 +1030,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/virtual-network/subnet/main.bicep b/avm/res/network/virtual-network/subnet/main.bicep index db1bac6476..a51b2d7c59 100644 --- a/avm/res/network/virtual-network/subnet/main.bicep +++ b/avm/res/network/virtual-network/subnet/main.bicep @@ -68,7 +68,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/virtual-network/subnet/main.json b/avm/res/network/virtual-network/subnet/main.json index fa80b6adbd..f38d007cef 100644 --- a/avm/res/network/virtual-network/subnet/main.json +++ b/avm/res/network/virtual-network/subnet/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "9295856151641467142" + "templateHash": "5699372618313647761" }, "name": "Virtual Network Subnets", "description": "This module deploys a Virtual Network Subnet.", @@ -231,7 +231,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/virtual-network/version.json b/avm/res/network/virtual-network/version.json index 76049e1c4a..13669e6601 100644 --- a/avm/res/network/virtual-network/version.json +++ b/avm/res/network/virtual-network/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/virtual-wan/main.bicep b/avm/res/network/virtual-wan/main.bicep index 915fd9e1f5..cea9038c4c 100644 --- a/avm/res/network/virtual-wan/main.bicep +++ b/avm/res/network/virtual-wan/main.bicep @@ -44,7 +44,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/virtual-wan/main.json b/avm/res/network/virtual-wan/main.json index 7903c4664f..0f9c0b98c0 100644 --- a/avm/res/network/virtual-wan/main.json +++ b/avm/res/network/virtual-wan/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "13454102867116433780" + "templateHash": "1929991864597825044" }, "name": "Virtual WANs", "description": "This module deploys a Virtual WAN.", @@ -198,7 +198,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/virtual-wan/version.json b/avm/res/network/virtual-wan/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/network/virtual-wan/version.json +++ b/avm/res/network/virtual-wan/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/network/vpn-site/main.bicep b/avm/res/network/vpn-site/main.bicep index b0e6eb44ed..75ce01a0e5 100644 --- a/avm/res/network/vpn-site/main.bicep +++ b/avm/res/network/vpn-site/main.bicep @@ -52,7 +52,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/network/vpn-site/main.json b/avm/res/network/vpn-site/main.json index bb1a7614e9..cb95d6cd71 100644 --- a/avm/res/network/vpn-site/main.json +++ b/avm/res/network/vpn-site/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "7657353044034170169" + "templateHash": "12541699981253725279" }, "name": "VPN Sites", "description": "This module deploys a VPN Site.", @@ -221,7 +221,7 @@ "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/network/vpn-site/version.json b/avm/res/network/vpn-site/version.json index 729ac87673..76049e1c4a 100644 --- a/avm/res/network/vpn-site/version.json +++ b/avm/res/network/vpn-site/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] From 78010a03ae9cda6106d52ceb684c9426b4459d91 Mon Sep 17 00:00:00 2001 From: Seif Bassem <38246040+sebassem@users.noreply.github.com> Date: Sun, 8 Sep 2024 09:51:14 +0300 Subject: [PATCH 02/57] feat: New Module avm-ptn-dev-ops-cicd-agents-and-runners (#3153) ## Description This pattern module will deploy self-hosted Azure DevOps Agents and / or GitHub Runners allowing selection of one of the following compute options: Azure Container Instance Azure Container Apps ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.dev-ops.cicd-agents-and-runners](https://github.com/sebassem/bicep-registry-modules/actions/workflows/avm.ptn.dev-ops.cicd-agents-and-runners.yml/badge.svg)](https://github.com/sebassem/bicep-registry-modules/actions/workflows/avm.ptn.dev-ops.cicd-agents-and-runners.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [X] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Jack Tracey <41163455+jtracey93@users.noreply.github.com> Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> Co-authored-by: Felipe Binotto --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + ...vm.ptn.dev-ops.cicd-agents-and-runners.yml | 84 + .../dev-ops/cicd-agents-and-runners/README.md | 981 + .../cicd-agents-and-runners/main.bicep | 1021 + .../dev-ops/cicd-agents-and-runners/main.json | 24830 ++++++++++++++++ .../scripts/azure-devops-agent-aca/README.md | 30 + .../scripts/azure-devops-agent-aca/dockerfile | 39 + .../scripts/azure-devops-agent-aca/start.sh | 115 + .../scripts/azure-devops-agent-aci/README.md | 19 + .../scripts/azure-devops-agent-aci/dockerfile | 29 + .../scripts/azure-devops-agent-aci/start.sh | 98 + .../scripts/github-runner-aca/README.md | 33 + .../scripts/github-runner-aca/app_token.sh | 89 + .../scripts/github-runner-aca/dockerfile | 49 + .../scripts/github-runner-aca/entrypoint.sh | 212 + .../github-runner-aca/install_actions.sh | 23 + .../scripts/github-runner-aca/token.sh | 44 + .../github-runner-aci/ConfigureAndRun.ps1 | 65 + .../github-runner-aci/InstallAgent.ps1 | 19 + .../scripts/github-runner-aci/README.md | 23 + .../scripts/github-runner-aci/dockerfile | 35 + .../scripts/startAzureDevOpsContainerJob.ps1 | 36 + .../tests/e2e/aca-aci/main.test.bicep | 63 + .../defaults.azuredevops.aci/main.test.bicep | 62 + .../e2e/defaults.github.aca/main.test.bicep | 61 + .../e2e/max.azuredevops.aca/main.test.bicep | 73 + .../tests/e2e/max.github.aci/main.test.bicep | 74 + .../dependencies.bicep | 105 + .../main.test.bicep | 87 + .../dependencies.bicep | 145 + .../main.test.bicep | 86 + .../private-github-new-vnet/main.test.bicep | 62 + .../cicd-agents-and-runners/version.json | 7 + 34 files changed, 28701 insertions(+) create mode 100644 .github/workflows/avm.ptn.dev-ops.cicd-agents-and-runners.yml create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/README.md create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/main.bicep create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/main.json create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aca/README.md create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aca/dockerfile create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aca/start.sh create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aci/README.md create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aci/dockerfile create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aci/start.sh create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/README.md create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/app_token.sh create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/dockerfile create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/entrypoint.sh create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/install_actions.sh create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/token.sh create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aci/ConfigureAndRun.ps1 create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aci/InstallAgent.ps1 create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aci/README.md create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aci/dockerfile create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/scripts/startAzureDevOpsContainerJob.ps1 create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/aca-aci/main.test.bicep create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/defaults.azuredevops.aci/main.test.bicep create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/defaults.github.aca/main.test.bicep create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/max.azuredevops.aca/main.test.bicep create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/max.github.aci/main.test.bicep create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-devops-existing-vnet-aci/dependencies.bicep create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-devops-existing-vnet-aci/main.test.bicep create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-github-existing-vnet-aca/dependencies.bicep create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-github-existing-vnet-aca/main.test.bicep create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-github-new-vnet/main.test.bicep create mode 100644 avm/ptn/dev-ops/cicd-agents-and-runners/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 31e04d58e8..6614ebf208 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -14,6 +14,7 @@ #/avm/ptn/avd-lza/session-hosts/ @Azure/avm-ptn-avd-lza-sessionhosts-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/azd/container-apps/ @Azure/avm-ptn-azd-containerapps-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/deployment-script/import-image-to-acr/ @Azure/avm-ptn-deploymentscript-importimagetoacr-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/ptn/dev-ops/cicd-agents-and-runners/ @Azure/avm-ptn-devops-cicdagentsandrunners-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/finops-toolkit/finops-hub/ @Azure/avm-ptn-finopstoolkit-finopshub-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/lz/sub-vending/ @Azure/avm-ptn-lz-subvending-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/network/private-link-private-dns-zones/ @Azure/avm-ptn-network-privatelinkprivatednszones-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index ef63ce6e6e..51cfae077b 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -49,6 +49,7 @@ body: # - "avm/ptn/avd-lza/session-hosts" - "avm/ptn/azd/container-apps" - "avm/ptn/deployment-script/import-image-to-acr" + - "avm/ptn/dev-ops/cicd-agents-and-runners" - "avm/ptn/finops-toolkit/finops-hub" - "avm/ptn/lz/sub-vending" - "avm/ptn/network/private-link-private-dns-zones" diff --git a/.github/workflows/avm.ptn.dev-ops.cicd-agents-and-runners.yml b/.github/workflows/avm.ptn.dev-ops.cicd-agents-and-runners.yml new file mode 100644 index 0000000000..b5e40f98ae --- /dev/null +++ b/.github/workflows/avm.ptn.dev-ops.cicd-agents-and-runners.yml @@ -0,0 +1,84 @@ +name: "avm.ptn.dev-ops.cicd-agents-and-runners" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.ptn.dev-ops.cicd-agents-and-runners" + - "avm/ptn/dev-ops/cicd-agents-and-runners/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/ptn/dev-ops/cicd-agents-and-runners" + workflowPath: ".github/workflows/avm.ptn.dev-ops.cicd-agents-and-runners.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/README.md b/avm/ptn/dev-ops/cicd-agents-and-runners/README.md new file mode 100644 index 0000000000..74b97be263 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/README.md @@ -0,0 +1,981 @@ +# CI CD Agents and Runners `[DevOps/CicdAgentsAndRunners]` + +This module deploys self-hosted agents and runners for Azure DevOps and GitHub on Azure Container Instances and/or Azure Container Apps. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.App/jobs` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/2024-03-01/jobs) | +| `Microsoft.App/managedEnvironments` | [2023-11-02-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/2023-11-02-preview/managedEnvironments) | +| `Microsoft.App/managedEnvironments/storages` | [2023-11-02-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/2023-11-02-preview/managedEnvironments/storages) | +| `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.ContainerInstance/containerGroups` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerInstance/2023-05-01/containerGroups) | +| `Microsoft.ContainerRegistry/registries` | [2023-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/registries) | +| `Microsoft.ContainerRegistry/registries/cacheRules` | [2023-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/registries/cacheRules) | +| `Microsoft.ContainerRegistry/registries/credentialSets` | [2023-11-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/registries/credentialSets) | +| `Microsoft.ContainerRegistry/registries/replications` | [2023-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/registries/replications) | +| `Microsoft.ContainerRegistry/registries/scopeMaps` | [2023-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/registries/scopeMaps) | +| `Microsoft.ContainerRegistry/registries/taskRuns` | [2019-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/2019-06-01-preview/registries/taskRuns) | +| `Microsoft.ContainerRegistry/registries/tasks` | [2019-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/2019-06-01-preview/registries/tasks) | +| `Microsoft.ContainerRegistry/registries/webhooks` | [2023-06-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ContainerRegistry/registries/webhooks) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.KeyVault/vaults/secrets` | [2023-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2023-07-01/vaults/secrets) | +| `Microsoft.ManagedIdentity/userAssignedIdentities` | [2023-01-31](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ManagedIdentity/2023-01-31/userAssignedIdentities) | +| `Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials` | [2023-01-31](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ManagedIdentity/2023-01-31/userAssignedIdentities/federatedIdentityCredentials) | +| `Microsoft.Network/natGateways` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/natGateways) | +| `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) | +| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/publicIPAddresses` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-09-01/publicIPAddresses) | +| `Microsoft.Network/publicIPPrefixes` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-09-01/publicIPPrefixes) | +| `Microsoft.Network/virtualNetworks` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualNetworks) | +| `Microsoft.Network/virtualNetworks/subnets` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualNetworks/subnets) | +| `Microsoft.Network/virtualNetworks/virtualNetworkPeerings` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualNetworks/virtualNetworkPeerings) | +| `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) | +| `Microsoft.Resources/deploymentScripts` | [2023-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Resources/2023-08-01/deploymentScripts) | +| `Microsoft.Storage/storageAccounts` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts) | +| `Microsoft.Storage/storageAccounts/blobServices` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices) | +| `Microsoft.Storage/storageAccounts/blobServices/containers` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers) | +| `Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers/immutabilityPolicies) | +| `Microsoft.Storage/storageAccounts/fileServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/fileServices) | +| `Microsoft.Storage/storageAccounts/fileServices/shares` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-01-01/storageAccounts/fileServices/shares) | +| `Microsoft.Storage/storageAccounts/localUsers` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/localUsers) | +| `Microsoft.Storage/storageAccounts/managementPolicies` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-01-01/storageAccounts/managementPolicies) | +| `Microsoft.Storage/storageAccounts/queueServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/queueServices) | +| `Microsoft.Storage/storageAccounts/queueServices/queues` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/queueServices/queues) | +| `Microsoft.Storage/storageAccounts/tableServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/tableServices) | +| `Microsoft.Storage/storageAccounts/tableServices/tables` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/tableServices/tables) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/ptn/dev-ops/cicd-agents-and-runners:`. + +- [Using only defaults for Azure DevOps self-hosted agents using both Azure Container Instances and Azure Container Apps.](#example-1-using-only-defaults-for-azure-devops-self-hosted-agents-using-both-azure-container-instances-and-azure-container-apps) +- [Using only defaults for Azure DevOps self-hosted agents using Azure Container Instances.](#example-2-using-only-defaults-for-azure-devops-self-hosted-agents-using-azure-container-instances) +- [Using only defaults for GitHub self-hosted runners using Azure Container Apps.](#example-3-using-only-defaults-for-github-self-hosted-runners-using-azure-container-apps) +- [Using large parameter set for Azure DevOps self-hosted agents using Azure Container Apps.](#example-4-using-large-parameter-set-for-azure-devops-self-hosted-agents-using-azure-container-apps) +- [Using large parameter set for GitHub self-hosted runners using Azure Container Instances.](#example-5-using-large-parameter-set-for-github-self-hosted-runners-using-azure-container-instances) +- [Using only defaults for Azure DevOps self-hosted agents using Private networking in an existing vnet.](#example-6-using-only-defaults-for-azure-devops-self-hosted-agents-using-private-networking-in-an-existing-vnet) +- [Using only defaults for GitHub self-hosted runners using Private networking in an existing vnet.](#example-7-using-only-defaults-for-github-self-hosted-runners-using-private-networking-in-an-existing-vnet) +- [Using only defaults for GitHub self-hosted runners using Private networking.](#example-8-using-only-defaults-for-github-self-hosted-runners-using-private-networking) + +### Example 1: _Using only defaults for Azure DevOps self-hosted agents using both Azure Container Instances and Azure Container Apps._ + +This instance deploys the module with the minimum set of required parameters for Azure DevOps self-hosted agents in Azure Container Instances and Azure Container Apps. + + +
+ +via Bicep module + +```bicep +module cicdAgentsAndRunners 'br/public:avm/ptn/dev-ops/cicd-agents-and-runners:' = { + name: 'cicdAgentsAndRunnersDeployment' + params: { + // Required parameters + computeTypes: [ + 'azure-container-app' + 'azure-container-instance' + ] + namingPrefix: '' + networkingConfiguration: { + addressSpace: '10.0.0.0/16' + networkType: 'createNew' + virtualNetworkName: 'vnet-aca' + } + selfHostedConfig: { + agentsPoolName: 'agents-pool' + devOpsOrganization: 'azureDevOpsOrganization' + personalAccessToken: '' + selfHostedType: 'azuredevops' + } + // Non-required parameters + location: '' + privateNetworking: false + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "computeTypes": { + "value": [ + "azure-container-app", + "azure-container-instance" + ] + }, + "namingPrefix": { + "value": "" + }, + "networkingConfiguration": { + "value": { + "addressSpace": "10.0.0.0/16", + "networkType": "createNew", + "virtualNetworkName": "vnet-aca" + } + }, + "selfHostedConfig": { + "value": { + "agentsPoolName": "agents-pool", + "devOpsOrganization": "azureDevOpsOrganization", + "personalAccessToken": "", + "selfHostedType": "azuredevops" + } + }, + // Non-required parameters + "location": { + "value": "" + }, + "privateNetworking": { + "value": false + } + } +} +``` + +
+

+ +### Example 2: _Using only defaults for Azure DevOps self-hosted agents using Azure Container Instances._ + +This instance deploys the module with the minimum set of required parameters for Azure DevOps self-hosted agents in Azure Container Instances. + + +

+ +via Bicep module + +```bicep +module cicdAgentsAndRunners 'br/public:avm/ptn/dev-ops/cicd-agents-and-runners:' = { + name: 'cicdAgentsAndRunnersDeployment' + params: { + // Required parameters + computeTypes: [ + 'azure-container-instance' + ] + namingPrefix: '' + networkingConfiguration: { + addressSpace: '10.0.0.0/16' + networkType: 'createNew' + virtualNetworkName: 'vnet-aci' + } + selfHostedConfig: { + agentsPoolName: 'aci-pool' + devOpsOrganization: 'azureDevOpsOrganization' + personalAccessToken: '' + selfHostedType: 'azuredevops' + } + // Non-required parameters + location: '' + privateNetworking: false + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "computeTypes": { + "value": [ + "azure-container-instance" + ] + }, + "namingPrefix": { + "value": "" + }, + "networkingConfiguration": { + "value": { + "addressSpace": "10.0.0.0/16", + "networkType": "createNew", + "virtualNetworkName": "vnet-aci" + } + }, + "selfHostedConfig": { + "value": { + "agentsPoolName": "aci-pool", + "devOpsOrganization": "azureDevOpsOrganization", + "personalAccessToken": "", + "selfHostedType": "azuredevops" + } + }, + // Non-required parameters + "location": { + "value": "" + }, + "privateNetworking": { + "value": false + } + } +} +``` + +
+

+ +### Example 3: _Using only defaults for GitHub self-hosted runners using Azure Container Apps._ + +This instance deploys the module with the minimum set of required parameters for GitHub self-hosted runners in Azure Container Apps. + + +

+ +via Bicep module + +```bicep +module cicdAgentsAndRunners 'br/public:avm/ptn/dev-ops/cicd-agents-and-runners:' = { + name: 'cicdAgentsAndRunnersDeployment' + params: { + // Required parameters + computeTypes: [ + 'azure-container-app' + ] + namingPrefix: '' + networkingConfiguration: { + addressSpace: '10.0.0.0/16' + networkType: 'createNew' + virtualNetworkName: 'vnet-aca' + } + selfHostedConfig: { + githubOrganization: 'githHubOrganization' + githubRepository: 'dummyRepo' + personalAccessToken: '' + selfHostedType: 'github' + } + // Non-required parameters + location: '' + privateNetworking: false + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "computeTypes": { + "value": [ + "azure-container-app" + ] + }, + "namingPrefix": { + "value": "" + }, + "networkingConfiguration": { + "value": { + "addressSpace": "10.0.0.0/16", + "networkType": "createNew", + "virtualNetworkName": "vnet-aca" + } + }, + "selfHostedConfig": { + "value": { + "githubOrganization": "githHubOrganization", + "githubRepository": "dummyRepo", + "personalAccessToken": "", + "selfHostedType": "github" + } + }, + // Non-required parameters + "location": { + "value": "" + }, + "privateNetworking": { + "value": false + } + } +} +``` + +
+

+ +### Example 4: _Using large parameter set for Azure DevOps self-hosted agents using Azure Container Apps._ + +This instance deploys the module with most of its features enabled for Azure DevOps self-hosted agents using Azure Container Apps. + + +

+ +via Bicep module + +```bicep +module cicdAgentsAndRunners 'br/public:avm/ptn/dev-ops/cicd-agents-and-runners:' = { + name: 'cicdAgentsAndRunnersDeployment' + params: { + // Required parameters + computeTypes: [ + 'azure-container-app' + ] + namingPrefix: '' + networkingConfiguration: { + addressSpace: '10.0.0.0/16' + containerAppSubnetAddressPrefix: '10.0.1.0/24' + containerAppSubnetName: 'acaSubnet' + networkType: 'createNew' + virtualNetworkName: 'vnet-aca' + } + selfHostedConfig: { + agentNamePrefix: '' + agentsPoolName: 'aca-pool' + azureContainerAppTarget: { + resources: { + cpu: '1' + memory: '2Gi' + } + } + devOpsOrganization: 'azureDevOpsOrganization' + personalAccessToken: '' + placeHolderAgentName: 'acaPlaceHolderAgent' + selfHostedType: 'azuredevops' + targetPipelinesQueueLength: '1' + } + // Non-required parameters + location: '' + privateNetworking: false + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "computeTypes": { + "value": [ + "azure-container-app" + ] + }, + "namingPrefix": { + "value": "" + }, + "networkingConfiguration": { + "value": { + "addressSpace": "10.0.0.0/16", + "containerAppSubnetAddressPrefix": "10.0.1.0/24", + "containerAppSubnetName": "acaSubnet", + "networkType": "createNew", + "virtualNetworkName": "vnet-aca" + } + }, + "selfHostedConfig": { + "value": { + "agentNamePrefix": "", + "agentsPoolName": "aca-pool", + "azureContainerAppTarget": { + "resources": { + "cpu": "1", + "memory": "2Gi" + } + }, + "devOpsOrganization": "azureDevOpsOrganization", + "personalAccessToken": "", + "placeHolderAgentName": "acaPlaceHolderAgent", + "selfHostedType": "azuredevops", + "targetPipelinesQueueLength": "1" + } + }, + // Non-required parameters + "location": { + "value": "" + }, + "privateNetworking": { + "value": false + } + } +} +``` + +
+

+ +### Example 5: _Using large parameter set for GitHub self-hosted runners using Azure Container Instances._ + +This instance deploys the module with most of its features enabled for GitHub self-hosted runners using Azure Container Instances. + + +

+ +via Bicep module + +```bicep +module cicdAgentsAndRunners 'br/public:avm/ptn/dev-ops/cicd-agents-and-runners:' = { + name: 'cicdAgentsAndRunnersDeployment' + params: { + // Required parameters + computeTypes: [ + 'azure-container-instance' + ] + namingPrefix: '' + networkingConfiguration: { + addressSpace: '10.0.0.0/16' + containerInstanceSubnetAddressPrefix: '10.0.1.0/24' + containerInstanceSubnetName: 'aci-subnet' + networkType: 'createNew' + virtualNetworkName: 'vnet-aci' + } + selfHostedConfig: { + azureContainerInstanceTarget: { + cpu: 1 + memoryInGB: 2 + numberOfInstances: 3 + sku: 'Standard' + } + ephemeral: true + githubOrganization: 'githHubOrganization' + githubRepository: 'dummyRepo' + personalAccessToken: '' + runnerNamePrefix: '' + runnerScope: 'repo' + selfHostedType: 'github' + targetWorkflowQueueLength: '1' + } + // Non-required parameters + location: '' + privateNetworking: false + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "computeTypes": { + "value": [ + "azure-container-instance" + ] + }, + "namingPrefix": { + "value": "" + }, + "networkingConfiguration": { + "value": { + "addressSpace": "10.0.0.0/16", + "containerInstanceSubnetAddressPrefix": "10.0.1.0/24", + "containerInstanceSubnetName": "aci-subnet", + "networkType": "createNew", + "virtualNetworkName": "vnet-aci" + } + }, + "selfHostedConfig": { + "value": { + "azureContainerInstanceTarget": { + "cpu": 1, + "memoryInGB": 2, + "numberOfInstances": 3, + "sku": "Standard" + }, + "ephemeral": true, + "githubOrganization": "githHubOrganization", + "githubRepository": "dummyRepo", + "personalAccessToken": "", + "runnerNamePrefix": "", + "runnerScope": "repo", + "selfHostedType": "github", + "targetWorkflowQueueLength": "1" + } + }, + // Non-required parameters + "location": { + "value": "" + }, + "privateNetworking": { + "value": false + } + } +} +``` + +
+

+ +### Example 6: _Using only defaults for Azure DevOps self-hosted agents using Private networking in an existing vnet._ + +This instance deploys the module with the minimum set of required parameters Azure DevOps self-hosted agents using Private networking in Azure Container Instances in an existing vnet. + + +

+ +via Bicep module + +```bicep +module cicdAgentsAndRunners 'br/public:avm/ptn/dev-ops/cicd-agents-and-runners:' = { + name: 'cicdAgentsAndRunnersDeployment' + params: { + // Required parameters + computeTypes: [ + 'azure-container-instance' + ] + namingPrefix: '' + networkingConfiguration: { + computeNetworking: { + computeNetworkType: 'azureContainerInstance' + containerInstanceSubnetName: 'aci-subnet' + } + containerRegistryPrivateDnsZoneResourceId: '' + containerRegistryPrivateEndpointSubnetName: 'acr-subnet' + natGatewayPublicIpAddressResourceId: '' + natGatewayResourceId: '' + networkType: 'useExisting' + virtualNetworkResourceId: '' + } + selfHostedConfig: { + agentNamePrefix: '' + agentsPoolName: 'aci-pool' + azureContainerInstanceTarget: { + numberOfInstances: 2 + } + devOpsOrganization: 'azureDevOpsOrganization' + personalAccessToken: '' + selfHostedType: 'azuredevops' + } + // Non-required parameters + location: '' + privateNetworking: 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 + "computeTypes": { + "value": [ + "azure-container-instance" + ] + }, + "namingPrefix": { + "value": "" + }, + "networkingConfiguration": { + "value": { + "computeNetworking": { + "computeNetworkType": "azureContainerInstance", + "containerInstanceSubnetName": "aci-subnet" + }, + "containerRegistryPrivateDnsZoneResourceId": "", + "containerRegistryPrivateEndpointSubnetName": "acr-subnet", + "natGatewayPublicIpAddressResourceId": "", + "natGatewayResourceId": "", + "networkType": "useExisting", + "virtualNetworkResourceId": "" + } + }, + "selfHostedConfig": { + "value": { + "agentNamePrefix": "", + "agentsPoolName": "aci-pool", + "azureContainerInstanceTarget": { + "numberOfInstances": 2 + }, + "devOpsOrganization": "azureDevOpsOrganization", + "personalAccessToken": "", + "selfHostedType": "azuredevops" + } + }, + // Non-required parameters + "location": { + "value": "" + }, + "privateNetworking": { + "value": true + } + } +} +``` + +
+

+ +### Example 7: _Using only defaults for GitHub self-hosted runners using Private networking in an existing vnet._ + +This instance deploys the module with the minimum set of required parameters GitHub self-hosted runners using Private networking in Azure Container Apps in an existing vnet. + + +

+ +via Bicep module + +```bicep +module cicdAgentsAndRunners 'br/public:avm/ptn/dev-ops/cicd-agents-and-runners:' = { + name: 'cicdAgentsAndRunnersDeployment' + params: { + // Required parameters + computeTypes: [ + 'azure-container-instance' + ] + namingPrefix: '' + networkingConfiguration: { + computeNetworking: { + computeNetworkType: 'azureContainerApp' + containerAppDeploymentScriptSubnetName: 'aca-ds-subnet' + containerAppSubnetName: 'aca-subnet' + containerInstanceSubnetName: 'aci-subnet' + deploymentScriptPrivateDnsZoneResourceId: '' + } + containerRegistryPrivateDnsZoneResourceId: '' + containerRegistryPrivateEndpointSubnetName: 'acr-subnet' + natGatewayPublicIpAddressResourceId: '' + natGatewayResourceId: '' + networkType: 'useExisting' + virtualNetworkResourceId: '' + } + selfHostedConfig: { + githubOrganization: 'githHubOrganization' + githubRepository: 'dummyRepo' + personalAccessToken: '' + selfHostedType: 'github' + } + // Non-required parameters + location: '' + privateNetworking: 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 + "computeTypes": { + "value": [ + "azure-container-instance" + ] + }, + "namingPrefix": { + "value": "" + }, + "networkingConfiguration": { + "value": { + "computeNetworking": { + "computeNetworkType": "azureContainerApp", + "containerAppDeploymentScriptSubnetName": "aca-ds-subnet", + "containerAppSubnetName": "aca-subnet", + "containerInstanceSubnetName": "aci-subnet", + "deploymentScriptPrivateDnsZoneResourceId": "" + }, + "containerRegistryPrivateDnsZoneResourceId": "", + "containerRegistryPrivateEndpointSubnetName": "acr-subnet", + "natGatewayPublicIpAddressResourceId": "", + "natGatewayResourceId": "", + "networkType": "useExisting", + "virtualNetworkResourceId": "" + } + }, + "selfHostedConfig": { + "value": { + "githubOrganization": "githHubOrganization", + "githubRepository": "dummyRepo", + "personalAccessToken": "", + "selfHostedType": "github" + } + }, + // Non-required parameters + "location": { + "value": "" + }, + "privateNetworking": { + "value": true + } + } +} +``` + +
+

+ +### Example 8: _Using only defaults for GitHub self-hosted runners using Private networking._ + +This instance deploys the module with the minimum set of required parameters GitHub self-hosted runners using Private networking in Azure Container Instances. + + +

+ +via Bicep module + +```bicep +module cicdAgentsAndRunners 'br/public:avm/ptn/dev-ops/cicd-agents-and-runners:' = { + name: 'cicdAgentsAndRunnersDeployment' + params: { + // Required parameters + computeTypes: [ + 'azure-container-instance' + ] + namingPrefix: '' + networkingConfiguration: { + addressSpace: '10.0.0.0/16' + networkType: 'createNew' + virtualNetworkName: 'vnet-aci' + } + selfHostedConfig: { + githubOrganization: 'githHubOrganization' + githubRepository: 'dummyRepo' + personalAccessToken: '' + selfHostedType: 'github' + } + // Non-required parameters + location: '' + privateNetworking: 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 + "computeTypes": { + "value": [ + "azure-container-instance" + ] + }, + "namingPrefix": { + "value": "" + }, + "networkingConfiguration": { + "value": { + "addressSpace": "10.0.0.0/16", + "networkType": "createNew", + "virtualNetworkName": "vnet-aci" + } + }, + "selfHostedConfig": { + "value": { + "githubOrganization": "githHubOrganization", + "githubRepository": "dummyRepo", + "personalAccessToken": "", + "selfHostedType": "github" + } + }, + // Non-required parameters + "location": { + "value": "" + }, + "privateNetworking": { + "value": true + } + } +} +``` + +
+

+ +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`computeTypes`](#parameter-computetypes) | array | The compute target for the private runners. | +| [`namingPrefix`](#parameter-namingprefix) | string | Naming prefix to be used with naming the deployed resources. | +| [`networkingConfiguration`](#parameter-networkingconfiguration) | object | The networking configuration. | +| [`selfHostedConfig`](#parameter-selfhostedconfig) | object | The self-hosted runner configuration. This can be either GitHub or Azure DevOps. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`location`](#parameter-location) | string | Location for all resources. | +| [`privateNetworking`](#parameter-privatenetworking) | bool | Whether to use private or public networking for the Azure Container Registry. | + +### Parameter: `computeTypes` + +The compute target for the private runners. + +- Required: Yes +- Type: array +- Allowed: + ```Bicep + [ + 'azure-container-app' + 'azure-container-instance' + ] + ``` + +### Parameter: `namingPrefix` + +Naming prefix to be used with naming the deployed resources. + +- Required: Yes +- Type: string + +### Parameter: `networkingConfiguration` + +The networking configuration. + +- Required: Yes +- Type: object + +### Parameter: `selfHostedConfig` + +The self-hosted runner configuration. This can be either GitHub or Azure DevOps. + +- Required: Yes +- Type: object + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `location` + +Location for all resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `privateNetworking` + +Whether to use private or public networking for the Azure Container Registry. + +- Required: No +- Type: bool +- Default: `True` + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the module was deployed to. | +| `resourceGroupName` | string | The name of the resource group the module was deployed to. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `br/public:avm/ptn/authorization/resource-role-assignment:0.1.1` | Remote reference | +| `br/public:avm/res/app/job:0.4.0` | Remote reference | +| `br/public:avm/res/app/managed-environment:0.6.2` | Remote reference | +| `br/public:avm/res/container-instance/container-group:0.2.0` | Remote reference | +| `br/public:avm/res/container-registry/registry:0.4.0` | Remote reference | +| `br/public:avm/res/managed-identity/user-assigned-identity:0.3.0` | Remote reference | +| `br/public:avm/res/network/nat-gateway:1.1.0` | Remote reference | +| `br/public:avm/res/network/private-dns-zone:0.5.0` | Remote reference | +| `br/public:avm/res/network/public-ip-address:0.5.1` | Remote reference | +| `br/public:avm/res/network/virtual-network:0.2.0` | Remote reference | +| `br/public:avm/res/operational-insights/workspace:0.5.0` | Remote reference | +| `br/public:avm/res/resources/deployment-script:0.3.1` | Remote reference | +| `br/public:avm/res/storage/storage-account:0.13.0` | Remote reference | + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/main.bicep b/avm/ptn/dev-ops/cicd-agents-and-runners/main.bicep new file mode 100644 index 0000000000..c14cdcf760 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/main.bicep @@ -0,0 +1,1021 @@ +metadata name = 'CI CD Agents and Runners' +metadata description = 'This module deploys self-hosted agents and runners for Azure DevOps and GitHub on Azure Container Instances and/or Azure Container Apps.' +metadata owner = 'Azure/module-maintainers' + +// ================ // +// Parameters // +// ================ // + +@description('Optional. Location for all resources.') +param location string = resourceGroup().location + +@description('Required. Naming prefix to be used with naming the deployed resources.') +param namingPrefix string + +@description('Required. The compute target for the private runners.') +param computeTypes computeTypesType + +@description('Required. The self-hosted runner configuration. This can be either GitHub or Azure DevOps.') +param selfHostedConfig selfHostedRunnerType + +@description('Required. The networking configuration.') +param networkingConfiguration networkType + +@description('Optional. Whether to use private or public networking for the Azure Container Registry.') +param privateNetworking bool = true + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +// ================ // +// Variables // +// ================ // + +var imagePaths = [ + { + platform: 'azuredevops-container-app' + imagePath: 'azure-devops-agent-aca' + } + { + platform: 'github-container-app' + imagePath: 'github-runner-aca' + } + { + platform: 'azuredevops-container-instance' + imagePath: 'azure-devops-agent-aci' + } + { + platform: 'github-container-instance' + imagePath: 'github-runner-aci' + } +] + +var githubURL = 'https://github.com/azure/bicep-registry-modules#main:avm/ptn/dev-ops/cicd-agents-and-runners/scripts' + +var acaGitHubRules = (selfHostedConfig.selfHostedType == 'github') + ? [ + { + name: 'github-runner-scaling-rule' + type: 'github-runner' + metadata: { + owner: selfHostedConfig.githubOrganization + repos: selfHostedConfig.githubRepository + targetWorkflowQueueLength: selfHostedConfig.?targetWorkflowQueueLength ?? '1' + runnerScope: selfHostedConfig.?runnerScope ?? 'repo' + } + auth: [ + { + secretRef: 'personal-access-token' + triggerParameter: 'personalAccessToken' + } + ] + } + ] + : [] + +var acaGitHubSecrets = (selfHostedConfig.selfHostedType == 'github') + ? [ + { + name: 'personal-access-token' + value: any(selfHostedConfig.personalAccessToken) + } + ] + : [] + +var acaGitHubEnvVariables = (selfHostedConfig.selfHostedType == 'github') + ? [ + { + name: 'RUNNER_NAME_PREFIX' + value: selfHostedConfig.?runnerNamePrefix ?? 'gh-runner' + } + { + name: 'REPO_URL' + value: gitHubRunnerURL + } + { + name: 'RUNNER_SCOPE' + value: selfHostedConfig.?runnerScope ?? 'repo' + } + { + name: 'EPHEMERAL' + value: selfHostedConfig.?ephemeral ?? 'true' + } + { + name: 'ORG_NAME' + value: selfHostedConfig.?gitHubOrganization + } + { + name: 'RUNNER_GROUP' + value: selfHostedConfig.?runnerGroup ?? '' + } + { + name: 'ACCESS_TOKEN' + secretRef: 'personal-access-token' + } + ] + : [] + +var acaAzureDevOpsEnvVariables = (selfHostedConfig.selfHostedType == 'azuredevops') + ? [ + { + name: 'AZP_POOL' + value: selfHostedConfig.agentsPoolName + } + { + name: 'AZP_AGENT_NAME_PREFIX' + value: selfHostedConfig.?agentNamePrefix ?? 'aca' + } + { + name: 'AZP_URL' + secretRef: 'organization-url' + } + { + name: 'AZP_TOKEN' + secretRef: 'personal-access-token' + } + ] + : [] + +var acaAzureDevOpsSecrets = (selfHostedConfig.selfHostedType == 'azuredevops') + ? [ + { + name: 'personal-access-token' + value: selfHostedConfig.personalAccessToken + } + { + name: 'organization-url' + value: devOpsOrgURL + } + ] + : [] + +var acaAzureDevOpsRules = (selfHostedConfig.selfHostedType == 'azuredevops') + ? [ + { + name: 'azure-pipelines-scaling-rule' + type: 'azure-pipelines' + metadata: { + poolName: selfHostedConfig.agentsPoolName + targetPipelinesQueueLength: selfHostedConfig.?targetPipelinesQueueLength ?? '1' + } + auth: [ + { + secretRef: 'personal-access-token' + triggerParameter: 'personalAccessToken' + } + { + secretRef: 'organization-url' + triggerParameter: 'organizationURL' + } + ] + } + ] + : [] + +var gitHubRunnerURL = 'https://github.com/${selfHostedConfig.?gitHubOrganization}/${selfHostedConfig.?gitHubRepository}' + +var devOpsOrgURL = 'https://dev.azure.com/${selfHostedConfig.?devOpsOrganization}' + +// ================ // +// Resources // +// ================ // + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { + name: '46d3xbcp.ptn.devops-cicdagentsandrunners.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } +} + +module logAnalyticsWokrspace 'br/public:avm/res/operational-insights/workspace:0.5.0' = { + name: 'logAnalyticsWokrspace' + params: { + name: 'law-${namingPrefix}-${uniqueString(resourceGroup().id)}-law' + } +} + +module userAssignedIdentity 'br/public:avm/res/managed-identity/user-assigned-identity:0.3.0' = { + name: 'userAssignedIdentity' + params: { + name: 'msi-${namingPrefix}-${uniqueString(resourceGroup().id)}' + location: location + } +} + +module acrPrivateDNSZone 'br/public:avm/res/network/private-dns-zone:0.5.0' = if (privateNetworking && empty(networkingConfiguration.?containerRegistryPrivateDnsZoneResourceId ?? '')) { + name: 'acrdnszone${namingPrefix}${uniqueString(resourceGroup().id)}' + params: { + name: 'privatelink.azurecr.io' + virtualNetworkLinks: [ + { + virtualNetworkResourceId: networkingConfiguration.networkType == 'createNew' + ? newVnet.outputs.resourceId + : networkingConfiguration.virtualNetworkResourceId + } + ] + } +} + +module acr 'br/public:avm/res/container-registry/registry:0.4.0' = { + name: 'acr${namingPrefix}${uniqueString(resourceGroup().id)}' + params: { + name: 'acr${namingPrefix}${uniqueString(resourceGroup().id)}' + acrSku: privateNetworking ? 'Premium' : 'Standard' + acrAdminUserEnabled: false + // Assigning AcrPull and AcrPush roles to the user assigned identity + roleAssignments: [ + { + principalId: userAssignedIdentity.outputs.principalId + roleDefinitionIdOrName: '7f951dda-4ed3-4680-a7ca-43fe172d538d' + } + { + principalId: userAssignedIdentity.outputs.principalId + roleDefinitionIdOrName: '8311e382-0749-4cb8-b61a-304f252e45ec' + } + ] + networkRuleBypassOptions: privateNetworking ? 'AzureServices' : 'None' + privateEndpoints: privateNetworking + ? [ + { + subnetResourceId: networkingConfiguration.networkType == 'createNew' + ? filter( + newVnet.outputs.subnetResourceIds, + subnetId => + contains( + subnetId, + networkingConfiguration.?containerRegistryPrivateEndpointSubnetName ?? 'acr-subnet' + ) + )[0] + : networkingConfiguration.networkType == 'useExisting' + ? '${networkingConfiguration.virtualNetworkResourceId}/subnets/${networkingConfiguration.containerRegistryPrivateEndpointSubnetName}' + : null + privateDnsZoneResourceIds: !empty(networkingConfiguration.?containerRegistryPrivateDnsZoneResourceId ?? '') + ? [ + networkingConfiguration.?containerRegistryPrivateDnsZoneResourceId ?? '' + ] + : [ + empty(networkingConfiguration.?containerRegistryPrivateDnsZoneResourceId ?? '') + ? acrPrivateDNSZone.outputs.resourceId + : '' + ] + privateDnsZoneGroupName: 'acrPrivateDNSZoneGroup' + } + ] + : null + } +} +module newVnet 'br/public:avm/res/network/virtual-network:0.2.0' = if (networkingConfiguration.networkType == 'createNew') { + name: 'vnet-${uniqueString(resourceGroup().id)}' + params: { + name: 'vnet-${namingPrefix}-${uniqueString(resourceGroup().id)}' + location: location + addressPrefixes: [ + networkingConfiguration.addressSpace + ] + subnets: union( + privateNetworking + ? [ + { + name: networkingConfiguration.?containerRegistryPrivateEndpointSubnetName ?? 'acr-subnet' + addressPrefix: networkingConfiguration.?containerRegistrySubnetPrefix ?? '10.0.0.32/29' + } + ] + : [], + (privateNetworking && selfHostedConfig.selfHostedType == 'azuredevops') || (contains( + computeTypes, + 'azure-container-instance' + )) + ? [ + { + name: networkingConfiguration.?containerInstanceSubnetName ?? 'aci-subnet' + addressPrefix: networkingConfiguration.?containerInstanceSubnetAddressPrefix ?? '10.0.2.0/24' + natGatewayResourceId: empty(networkingConfiguration.?natGatewayResourceId ?? '') && privateNetworking + ? natGateway.outputs.resourceId + : networkingConfiguration.?natGatewayResourceId ?? '' + delegations: [ + { + name: 'Microsoft.ContainerInstance/containerGroups' + properties: { + serviceName: 'Microsoft.ContainerInstance/containerGroups' + } + } + ] + } + ] + : [], + contains(computeTypes, 'azure-container-app') + ? [ + { + name: networkingConfiguration.?containerAppSubnetName ?? 'app-subnet' + addressPrefix: networkingConfiguration.?containerAppSubnetAddressPrefix ?? '10.0.1.0/24' + natGatewayResourceId: empty(networkingConfiguration.?natGatewayResourceId ?? '') && privateNetworking + ? natGateway.outputs.resourceId + : networkingConfiguration.?natGatewayResourceId ?? '' + delegations: [ + { + name: 'Microsoft.App.environments' + properties: { + serviceName: 'Microsoft.App/environments' + } + } + ] + } + ] + : [], + contains(computeTypes, 'azure-container-app') && privateNetworking && selfHostedConfig.selfHostedType == 'azuredevops' + ? [ + { + name: networkingConfiguration.?containerAppDeploymentScriptSubnetName ?? 'app-deployment-script-subnet' + addressPrefix: networkingConfiguration.?containerAppDeploymentScriptSubnetPrefix ?? '10.0.3.0/29' + } + ] + : [], + [] + ) + } +} +module appEnvironment 'br/public:avm/res/app/managed-environment:0.6.2' = if (contains( + computeTypes, + 'azure-container-app' +)) { + name: 'appEnv-${uniqueString(resourceGroup().id)}' + params: { + name: 'appEnv${namingPrefix}${uniqueString(resourceGroup().id)}' + logAnalyticsWorkspaceResourceId: logAnalyticsWokrspace.outputs.resourceId + location: location + infrastructureSubnetId: networkingConfiguration.networkType == 'createNew' + ? filter( + newVnet.outputs.subnetResourceIds, + subnetId => contains(subnetId, networkingConfiguration.?containerAppSubnetName ?? 'app-subnet') + )[0] + : networkingConfiguration.networkType == 'useExisting' + ? '${networkingConfiguration.virtualNetworkResourceId}/subnets/${networkingConfiguration.computeNetworking.containerAppSubnetName}' + : null + zoneRedundant: true + internal: privateNetworking ? true : false + workloadProfiles: [ + { + name: 'consumption' + workloadProfileType: 'consumption' + } + ] + } +} + +module natGatewayPublicIp 'br/public:avm/res/network/public-ip-address:0.5.1' = if (empty(networkingConfiguration.?natGatewayResourceId ?? '') && empty(networkingConfiguration.?natGatewayPublicIpAddressResourceId ?? '') && networkingConfiguration.networkType == 'createNew' && privateNetworking) { + name: 'natGatewayPublicIp-${uniqueString(resourceGroup().id)}' + params: { + name: 'natGatewayPublicIp-${uniqueString(resourceGroup().id)}' + skuName: 'Standard' + publicIPAddressVersion: 'IPv4' + publicIPAllocationMethod: 'Static' + } +} + +module natGateway 'br/public:avm/res/network/nat-gateway:1.1.0' = if (privateNetworking && empty(networkingConfiguration.?natGatewayResourceId ?? '') && networkingConfiguration.networkType == 'createNew') { + name: 'natGateway-${uniqueString(resourceGroup().id)}' + params: { + name: 'natGateway-${namingPrefix}-${uniqueString(resourceGroup().id)}' + zone: 0 + location: location + publicIpResourceIds: [ + networkingConfiguration.?natGatewayPublicIpAddressResourceId ?? natGatewayPublicIp.outputs.resourceId + ] + } +} + +resource buildImages 'Microsoft.ContainerRegistry/registries/tasks@2019-06-01-preview' = [ + for (image, i) in computeTypes: { + name: '${acr.name}/buildImage-${image}-${selfHostedConfig.selfHostedType}-${i}' + location: location + identity: { + type: 'SystemAssigned' + } + properties: { + platform: { + os: 'Linux' + } + step: { + dockerFilePath: 'dockerfile' + type: 'Docker' + contextPath: contains(computeTypes, 'azure-container-app') + ? '${githubURL}/${filter(imagePaths, imagePath => imagePath.platform == '${selfHostedConfig.selfHostedType}-container-app')[0].imagePath}' + : contains(computeTypes, 'azure-container-instance') + ? '${githubURL}/${filter(imagePaths, imagePath => imagePath.platform == '${selfHostedConfig.selfHostedType}-container-instance')[0].imagePath}' + : null + imageNames: [ + '${acr.outputs.loginServer}/${selfHostedConfig.selfHostedType}-${image}:latest' + ] + } + credentials: privateNetworking + ? { + customRegistries: { + '${acr.outputs.loginServer}': { + identity: '[system]' + } + } + } + : null + } + } +] + +module buildImagesRoleAssignment 'br/public:avm/ptn/authorization/resource-role-assignment:0.1.1' = [ + for (image, i) in computeTypes: { + name: 'buildImagesRoleAssignment-${uniqueString(resourceGroup().id)}-${i}' + params: { + principalId: buildImages[i].identity.principalId + resourceId: acr.outputs.resourceId + roleDefinitionId: '8311e382-0749-4cb8-b61a-304f252e45ec' + principalType: 'ServicePrincipal' + } + } +] + +resource taskRun 'Microsoft.ContainerRegistry/registries/taskRuns@2019-06-01-preview' = [ + for (image, i) in computeTypes: { + name: '${acr.name}/taskrun-${image}-${selfHostedConfig.selfHostedType}-${i}' + location: location + dependsOn: [ + buildImagesRoleAssignment + ] + properties: { + runRequest: { + taskId: buildImages[i].id + type: 'TaskRunRequest' + } + } + } +] + +module aciJob 'br/public:avm/res/container-instance/container-group:0.2.0' = [ + for i in range(0, int(selfHostedConfig.?azureContainerInstanceTarget.?numberOfInstances ?? 1)): if (contains( + computeTypes, + 'azure-container-instance' + )) { + name: '${namingPrefix}aciJob-${i}' + dependsOn: [ + taskRun + ] + params: { + name: '${namingPrefix}-${uniqueString(resourceGroup().id)}-instance-${i}' + managedIdentities: { + userAssignedResourceIds: [ + userAssignedIdentity.outputs.resourceId + ] + } + imageRegistryCredentials: [ + { + identity: userAssignedIdentity.outputs.resourceId + server: acr.outputs.loginServer + } + ] + subnetId: privateNetworking && networkingConfiguration.networkType == 'createNew' + ? filter( + newVnet.outputs.subnetResourceIds, + subnetId => contains(subnetId, networkingConfiguration.?containerInstanceSubnetName ?? 'aci-subnet') + )[0] + : privateNetworking && networkingConfiguration.networkType == 'useExisting' + ? '${networkingConfiguration.virtualNetworkResourceId}/subnets/${networkingConfiguration.computeNetworking.containerInstanceSubnetName}' + : null + ipAddressType: privateNetworking ? 'Private' : 'Public' + sku: 'Standard' + containers: [ + { + name: selfHostedConfig.selfHostedType == 'github' + ? 'private-runner-github-aci-${i}' + : selfHostedConfig.selfHostedType == 'azuredevops' + ? 'private-runner-devops-aci-${i}' + : 'private-runner-aci-${i}' + properties: { + image: '${acr.outputs.loginServer}/${selfHostedConfig.selfHostedType}-azure-container-instance:latest' + ports: [ + { + port: 80 + protocol: 'TCP' + } + ] + resources: { + requests: { + cpu: selfHostedConfig.?cpu ?? 1 + memoryInGB: selfHostedConfig.?memoryInGB ?? 2 + } + } + environmentVariables: selfHostedConfig.selfHostedType == 'github' + ? [ + { + name: 'GH_RUNNER_NAME' + value: any(selfHostedConfig.?runnerName ?? 'runner-${i}') + } + { + name: 'GH_RUNNER_URL' + value: gitHubRunnerURL + } + { + name: 'GH_RUNNER_GROUP' + value: selfHostedConfig.?runnerGroup != null ? '${selfHostedConfig.?runnerGroup}' : '' + } + { + name: 'GH_RUNNER_TOKEN' + secureValue: any(selfHostedConfig.personalAccessToken) + } + ] + : [ + { + name: 'AZP_URL' + value: devOpsOrgURL + } + { + name: 'AZP_POOL' + value: selfHostedConfig.agentsPoolName + } + { + name: 'AZP_AGENT_NAME' + value: any(selfHostedConfig.?agentName ?? 'agent-${i}') + } + { + name: 'AZP_TOKEN' + secureValue: any(selfHostedConfig.personalAccessToken) + } + ] + } + } + ] + ipAddressPorts: [ + { + port: 80 + protocol: 'TCP' + } + ] + } + } +] + +module acaJob 'br/public:avm/res/app/job:0.4.0' = if (contains(computeTypes, 'azure-container-app')) { + name: '${namingPrefix}acaJob' + dependsOn: [ + taskRun + runPlaceHolderAgent + ] + params: { + name: '${namingPrefix}-${uniqueString(resourceGroup().id)}-acajob' + location: location + managedIdentities: { + userAssignedResourceIds: [ + userAssignedIdentity.outputs.resourceId + ] + } + roleAssignments: [ + { + principalId: userAssignedIdentity.outputs.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + name: guid(userAssignedIdentity.outputs.principalId, 'Contributor', resourceGroup().id) + } + ] + triggerType: 'Event' + replicaRetryLimit: 3 + replicaTimeout: 1800 + eventTriggerConfig: { + parallelism: 1 + replicaCompletionCount: 1 + scale: { + maxExecutions: 100 + minExecutions: 0 + pollingInterval: 30 + rules: selfHostedConfig.selfHostedType == 'github' ? acaGitHubRules : acaAzureDevOpsRules + } + } + registries: [ + { + server: acr.outputs.loginServer + identity: userAssignedIdentity.outputs.resourceId + } + ] + secrets: selfHostedConfig.selfHostedType == 'github' ? acaGitHubSecrets : acaAzureDevOpsSecrets + containers: [ + { + image: '${acr.outputs.loginServer}/${selfHostedConfig.selfHostedType}-azure-container-app:latest' + name: 'private-runner-${selfHostedConfig.selfHostedType}-aca' + resources: selfHostedConfig.?azureContainerAppTarget.?resources ?? { + cpu: '1' + memory: '2Gi' + } + env: selfHostedConfig.selfHostedType == 'github' ? acaGitHubEnvVariables : acaAzureDevOpsEnvVariables + } + ] + environmentResourceId: appEnvironment.outputs.resourceId + workloadProfileName: 'consumption' + } +} + +module acaPlaceholderJob 'br/public:avm/res/app/job:0.4.0' = if (contains(computeTypes, 'azure-container-app') && selfHostedConfig.selfHostedType == 'azuredevops') { + name: 'acaDevOpsPlaceholderJob' + dependsOn: [ + taskRun + ] + params: { + name: '${namingPrefix}-${uniqueString(resourceGroup().id)}-placeholder' + location: location + managedIdentities: { + userAssignedResourceIds: [ + userAssignedIdentity.outputs.resourceId + ] + } + roleAssignments: [ + { + principalId: userAssignedIdentity.outputs.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + ] + triggerType: 'Manual' + replicaRetryLimit: 0 + replicaTimeout: 300 + manualTriggerConfig: { + parallelism: 1 + replicaCompletionCount: 1 + } + registries: [ + { + server: acr.outputs.loginServer + identity: userAssignedIdentity.outputs.resourceId + } + ] + secrets: acaAzureDevOpsSecrets + containers: [ + { + image: '${acr.outputs.loginServer}/${selfHostedConfig.selfHostedType}-azure-container-app:latest' + name: 'private-runner-devops-aca' + resources: { + cpu: '1' + memory: '2Gi' + } + env: [ + { + name: 'AZP_POOL' + value: selfHostedConfig.agentsPoolName + } + { + name: 'AZP_AGENT_NAME' + value: selfHostedConfig.?placeHolderAgentName ?? 'placeHolderAgent' + } + { + name: 'AZP_PLACEHOLDER' + value: '1' + } + { + name: 'AZP_URL' + secretRef: 'organization-url' + } + { + name: 'AZP_TOKEN' + secretRef: 'personal-access-token' + } + ] + } + ] + environmentResourceId: appEnvironment.outputs.resourceId + workloadProfileName: 'consumption' + } +} +module deploymentScriptPrivateDNSZone 'br/public:avm/res/network/private-dns-zone:0.5.0' = if (contains( + computeTypes, + 'azure-container-app' +) && selfHostedConfig.selfHostedType == 'azuredevops' && privateNetworking && empty(networkingConfiguration.computeNetworking.?deploymentScriptPrivateDnsZoneResourceId ?? '')) { + name: 'stgdsdnszone${namingPrefix}${uniqueString(resourceGroup().id)}' + params: { + name: 'privatelink.file.${environment().suffixes.storage}' + virtualNetworkLinks: [ + { + virtualNetworkResourceId: networkingConfiguration.networkType == 'useExisting' + ? networkingConfiguration.virtualNetworkResourceId + : newVnet.outputs.resourceId + } + ] + } +} + +module deploymentScriptStg 'br/public:avm/res/storage/storage-account:0.13.0' = if (contains( + computeTypes, + 'azure-container-app' +) && selfHostedConfig.selfHostedType == 'azuredevops' && privateNetworking) { + name: 'placeholderAgentDeploymentScript-${uniqueString(resourceGroup().id)}' + params: { + name: 'stgds${uniqueString(resourceGroup().id, selfHostedConfig.?placeHolderAgentName ?? 'placeHolderAgent',location,selfHostedConfig.agentsPoolName)}' + location: location + allowBlobPublicAccess: false + allowSharedKeyAccess: true + publicNetworkAccess: 'Disabled' + //Assigning Storage Blob Data Contributor role to the user assigned identity + roleAssignments: [ + { + principalId: userAssignedIdentity.outputs.principalId + roleDefinitionIdOrName: '69566ab7-960f-475b-8e7c-b3118f30c6bd' + principalType: 'ServicePrincipal' + } + ] + privateEndpoints: [ + { + service: 'file' + subnetResourceId: networkingConfiguration.networkType == 'useExisting' + ? '${networkingConfiguration.virtualNetworkResourceId}/subnets/${networkingConfiguration.computeNetworking.containerAppDeploymentScriptSubnetName}' + : filter( + newVnet.outputs.subnetResourceIds, + subnetId => + contains( + subnetId, + networkingConfiguration.?containerAppDeploymentScriptSubnetName ?? 'app-deployment-script-subnet' + ) + )[0] + privateDnsZoneGroupName: 'stgPrivateDNSZoneGroup' + privateDnsZoneResourceIds: [ + networkingConfiguration.computeNetworking.?deploymentScriptPrivateDnsZoneResourceId ?? deploymentScriptPrivateDNSZone.outputs.resourceId + ] + } + ] + } +} + +module runPlaceHolderAgent 'br/public:avm/res/resources/deployment-script:0.3.1' = if (contains( + computeTypes, + 'azure-container-app' +) && selfHostedConfig.selfHostedType == 'azuredevops') { + name: 'runPlaceHolderAgent-${uniqueString(resourceGroup().id)}' + params: { + name: 'runPlaceHolderAgent-${uniqueString(resourceGroup().id)}' + kind: 'AzurePowerShell' + azPowerShellVersion: '12.1' + cleanupPreference: 'Always' + retentionInterval: 'P1D' + location: location + managedIdentities: { + userAssignedResourcesIds: [ + userAssignedIdentity.outputs.resourceId + ] + } + storageAccountResourceId: privateNetworking ? deploymentScriptStg.outputs.resourceId : null + subnetResourceIds: privateNetworking && networkingConfiguration.networkType == 'createNew' + ? [ + filter( + newVnet.outputs.subnetResourceIds, + subnetId => contains(subnetId, networkingConfiguration.?containerInstanceSubnetName ?? 'aci-subnet') + )[0] + ] + : privateNetworking && networkingConfiguration.networkType == 'useExisting' + ? [ + '${networkingConfiguration.virtualNetworkResourceId}/subnets/${networkingConfiguration.computeNetworking.containerInstanceSubnetName}' + ] + : null + arguments: '-resourceGroup ${resourceGroup().name} -jobName ${acaPlaceholderJob.outputs.name} -subscriptionId ${subscription().subscriptionId}' + scriptContent: loadTextContent('./scripts/startAzureDevOpsContainerJob.ps1') + } +} + +// ================ // +// Outputs // +// ================ // + +@description('The name of the resource group the module was deployed to.') +output resourceGroupName string = resourceGroup().name + +@description('The location the module was deployed to.') +output location string = location + +// ================ // +// Definitions // +// ================ // + +type newNetworkType = { + @description('Required. The network type. This can be either createNew or useExisting.') + networkType: 'createNew' + + @description('Required. The virtual network name of the created virtual network.') + virtualNetworkName: string + + @description('Required. The address space of the created virtual network.') + addressSpace: string + + @description('Optional. The existing NAT Gateway resource Id. This should be provided if an existing NAT gateway is available to be used. If this parameter is not provided, a new NAT gateway will be created.') + natGatewayResourceId: string? + + @description('Optional. The existing public IP address to assosciate with the NAT gateway. This should be provided if an existing public Ip address is available to be used. If this parameter is not provided, a new Public Ip address will be created.') + natGatewayPublicIpAddressResourceId: string? + + @description('Optional. The container instance subnet name in the created virtual network. If not provided, a default name will be used.') + containerInstanceSubnetName: string? + + @description('Optional. The container instance subnet CIDR in the created virtual network. If not provided, a default subnet prefix will be used.') + containerInstanceSubnetAddressPrefix: string? + + @description('Optional. The container app subnet name in the created virtual network. If not provided, a default name will be used.') + containerAppSubnetName: string? + + @description('Optional. The container app subnet CIDR in the created virtual network. If not provided, a default subnet prefix will be used.') + containerAppSubnetAddressPrefix: string? + + @description('Optional. The container instance subnet address prefix in the created virtual network. If not provided, a default subnet prefix will be used.') + containerInstancesubnetPrefix: string? + + @description('Optional. The container registry subnet address prefix in the created virtual network. If not provided, a default subnet prefix will be used.') + containerRegistrySubnetPrefix: string? + + @description('Optional. The container registry private DNS zone Id. If not provided, a new private DNS zone will be created.') + containerRegistryPrivateDnsZoneResourceId: string? + + @description('Optional. The subnet name for the container registry private endpoint. If not provided, a default name will be used.') + containerRegistryPrivateEndpointSubnetName: string? + + @description('Optional. The subnet name for the container app deployment script. Only required if private networking is used. If not provided, a default name will be used.') + containerAppDeploymentScriptSubnetName: string? + + @description('Optional. The subnet address prefix for the container app deployment script which is used to start the placeholder Azure DevOps agent. Only required if private networking is used. If not provided, a default subnet prefix will be used.') + containerAppDeploymentScriptSubnetPrefix: string? + + @description('Optional. The deployment script private DNS zone Id. If not provided, a new private DNS zone will be created. Only required if private networking is used.') + deploymentScriptPrivateDnsZoneResourceId: string? +} + +type existingNetworkType = { + @description('Required. The network type. This can be either createNew or useExisting.') + networkType: 'useExisting' + + @description('Required. The existing virtual network resource Id.') + virtualNetworkResourceId: string + + @description('Required. The subnet name for the container registry private endpoint.') + containerRegistryPrivateEndpointSubnetName: string + + @description('Optional. The container registry private DNS zone Id. If not provided, a new private DNS zone will be created.') + containerRegistryPrivateDnsZoneResourceId: string? + + @description('Optional. The existing NAT Gateway resource Id. This should be provided if an existing NAT gateway is available to be used. If this parameter is not provided, a new NAT gateway will be created.') + natGatewayResourceId: string + + @description('Optional. The existing public IP address to assosciate with the NAT gateway. This should be provided if an existing public Ip address is available to be used. If this parameter is not provided, a new Public Ip address will be created.') + natGatewayPublicIpAddressResourceId: string + + @description('Required. The compute type networking type.') + computeNetworking: computeNetworkingType +} + +type containerAppNetworkConfigType = { + @description('Required. The Azure Container App networking type.') + computeNetworkType: 'azureContainerApp' + + @description('Required. The existing network container app subnet name. This is required for Container Apps compute type. This subnet needs to have service delegation for App environments.') + containerAppSubnetName: string + + @description('Required. The existing subnet name for the container app deployment script.') + containerAppDeploymentScriptSubnetName: string + + @description('Optional. The deployment script private DNS zone Id. If not provided, a new private DNS zone will be created.') + deploymentScriptPrivateDnsZoneResourceId: string? + + @description('Required. The container instance subnet name in the created virtual network. If not provided, a default name will be used. This subnet is required for private networking Azure DevOps scenarios to deploy the deployment script which starts the placeholder agent privately.') + containerInstanceSubnetName: string? +} + +type containerInstanceNetworkConfigType = { + @description('Required. The Azure Container Instance network type.') + computeNetworkType: 'azureContainerInstance' + + @description('Required. The container instance subnet name in the created virtual network. If not provided, a default name will be used.') + containerInstanceSubnetName: string +} + +@discriminator('networkType') +type networkType = newNetworkType | existingNetworkType + +@discriminator('computeNetworkType') +type computeNetworkingType = containerAppNetworkConfigType | containerInstanceNetworkConfigType + +type azureContainerInstanceTargetType = { + @description('Optional. The Azure Container Instance Sku name.') + sku: 'Standard' | 'Dedicated'? + + @description('Optional. The number of the Azure Container Instances to deploy.') + numberOfInstances: int? + + @description('Optional. The Azure Container Instance container cpu.') + cpu: int? + + @description('Optional. The Azure Container Instance container memory.') + memoryInGB: int? + + @description('Optional. The Azure Container Instance container port.') + port: int? +}? + +type azureContainerAppTargetType = { + @description('Optional. The Azure Container App Job CPU and memory resources.') + resources: acaResourcesType? +} + +type gitHubRunnersType = { + @description('Required. The self-hosted runner type.') + selfHostedType: 'github' + + @description('Required. The GitHub personal access token with permissions to create and manage self-hosted runners. See https://learn.microsoft.com/azure/container-apps/tutorial-ci-cd-runners-jobs?tabs=bash&pivots=container-apps-jobs-self-hosted-ci-cd-github-actions#get-a-github-personal-access-token for PAT permissions.') + @secure() + personalAccessToken: string + + @description('Required. The GitHub organization name.') + githubOrganization: string + + @description('Required. The GitHub repository name.') + githubRepository: string + + @description('Optional. The GitHub runner name.') + runnerName: string? + + @description('Optional. The GitHub runner group.') + runnerGroup: string? + + @description('Optional. The GitHub runner name prefix.') + runnerNamePrefix: string? + + @description('Optional. The GitHub runner scope.') + runnerScope: 'repo' | 'org' | 'ent'? + + @description('Optional. Deploy ephemeral runners.') + ephemeral: true? + + @description('Optional. The target workflow queue length.') + targetWorkflowQueueLength: string? + + @description('Optional. The GitHub runner Azure Container instance configuration.') + azureContainerInstanceTarget: azureContainerInstanceTargetType? + + @description('Optional. The GitHub runner Azure Container app configuration.') + azureContainerAppTarget: azureContainerAppTargetType? +} + +type devOpsAgentsType = { + @description('Required. The self-hosted runner type.') + selfHostedType: 'azuredevops' + + @description('Required. The Azure DevOps persoanl access token with permissions to create and manage self-hosted agents. See https://learn.microsoft.com/azure/container-apps/tutorial-ci-cd-runners-jobs?tabs=bash&pivots=container-apps-jobs-self-hosted-ci-cd-azure-pipelines#get-an-azure-devops-personal-access-token for PAT permissions.') + @secure() + personalAccessToken: string + + @description('Required. The Azure DevOps organization name.') + devOpsOrganization: string + + @description('Required. The Azure DevOps agents pool name.') + agentsPoolName: string + + @description('Optional. The Azure DevOps agent name.') + agentName: string? + + @description('Optional. The Azure DevOps agent name prefix.') + agentNamePrefix: string? + + @description('Optional. The Azure DevOps placeholder agent name.') + placeHolderAgentName: string? + + @description('Optional. The target pipelines queue length.') + targetPipelinesQueueLength: string? + + @description('Optional. The GitHub runner Azure Container instance configuration.') + azureContainerInstanceTarget: azureContainerInstanceTargetType? + + @description('Optional. The AzureDevOps agents Azure Container app configuration.') + azureContainerAppTarget: azureContainerAppTargetType? +} + +type acaResourcesType = + | { cpu: '0.25', memory: '0.5Gi' } + | { cpu: '0.5', memory: '1Gi' } + | { cpu: '0.75', memory: '1.5Gi' } + | { cpu: '1', memory: '2Gi' } + | { cpu: '1.25', memory: '2.5Gi' } + | { cpu: '1.5', memory: '3Gi' } + | { cpu: '1.75', memory: '3.5Gi' } + | { cpu: '2', memory: '4Gi' } + | { cpu: '2.25', memory: '4.5Gi' } + | { cpu: '2.5', memory: '5Gi' } + | { cpu: '2.75', memory: '5.5Gi' } + | { cpu: '3', memory: '6Gi' } + | { cpu: '3.25', memory: '6.5Gi' } + | { cpu: '3.5', memory: '7Gi' } + | { cpu: '3.75', memory: '7.5Gi' } + | { cpu: '4', memory: '8Gi' } + +@discriminator('selfHostedType') +type selfHostedRunnerType = gitHubRunnersType | devOpsAgentsType + +@description('Required. The target compute environments for the private runners.') +type computeTypesType = ('azure-container-app' | 'azure-container-instance')[] diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/main.json b/avm/ptn/dev-ops/cicd-agents-and-runners/main.json new file mode 100644 index 0000000000..2244e4e84c --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/main.json @@ -0,0 +1,24830 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "17412855215846424947" + }, + "name": "CI CD Agents and Runners", + "description": "This module deploys self-hosted agents and runners for Azure DevOps and GitHub on Azure Container Instances and/or Azure Container Apps.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "newNetworkType": { + "type": "object", + "properties": { + "networkType": { + "type": "string", + "allowedValues": [ + "createNew" + ], + "metadata": { + "description": "Required. The network type. This can be either createNew or useExisting." + } + }, + "virtualNetworkName": { + "type": "string", + "metadata": { + "description": "Required. The virtual network name of the created virtual network." + } + }, + "addressSpace": { + "type": "string", + "metadata": { + "description": "Required. The address space of the created virtual network." + } + }, + "natGatewayResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The existing NAT Gateway resource Id. This should be provided if an existing NAT gateway is available to be used. If this parameter is not provided, a new NAT gateway will be created." + } + }, + "natGatewayPublicIpAddressResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The existing public IP address to assosciate with the NAT gateway. This should be provided if an existing public Ip address is available to be used. If this parameter is not provided, a new Public Ip address will be created." + } + }, + "containerInstanceSubnetName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The container instance subnet name in the created virtual network. If not provided, a default name will be used." + } + }, + "containerInstanceSubnetAddressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The container instance subnet CIDR in the created virtual network. If not provided, a default subnet prefix will be used." + } + }, + "containerAppSubnetName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The container app subnet name in the created virtual network. If not provided, a default name will be used." + } + }, + "containerAppSubnetAddressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The container app subnet CIDR in the created virtual network. If not provided, a default subnet prefix will be used." + } + }, + "containerInstancesubnetPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The container instance subnet address prefix in the created virtual network. If not provided, a default subnet prefix will be used." + } + }, + "containerRegistrySubnetPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The container registry subnet address prefix in the created virtual network. If not provided, a default subnet prefix will be used." + } + }, + "containerRegistryPrivateDnsZoneResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The container registry private DNS zone Id. If not provided, a new private DNS zone will be created." + } + }, + "containerRegistryPrivateEndpointSubnetName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subnet name for the container registry private endpoint. If not provided, a default name will be used." + } + }, + "containerAppDeploymentScriptSubnetName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subnet name for the container app deployment script. Only required if private networking is used. If not provided, a default name will be used." + } + }, + "containerAppDeploymentScriptSubnetPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subnet address prefix for the container app deployment script which is used to start the placeholder Azure DevOps agent. Only required if private networking is used. If not provided, a default subnet prefix will be used." + } + }, + "deploymentScriptPrivateDnsZoneResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The deployment script private DNS zone Id. If not provided, a new private DNS zone will be created. Only required if private networking is used." + } + } + } + }, + "existingNetworkType": { + "type": "object", + "properties": { + "networkType": { + "type": "string", + "allowedValues": [ + "useExisting" + ], + "metadata": { + "description": "Required. The network type. This can be either createNew or useExisting." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The existing virtual network resource Id." + } + }, + "containerRegistryPrivateEndpointSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The subnet name for the container registry private endpoint." + } + }, + "containerRegistryPrivateDnsZoneResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The container registry private DNS zone Id. If not provided, a new private DNS zone will be created." + } + }, + "natGatewayResourceId": { + "type": "string", + "metadata": { + "description": "Optional. The existing NAT Gateway resource Id. This should be provided if an existing NAT gateway is available to be used. If this parameter is not provided, a new NAT gateway will be created." + } + }, + "natGatewayPublicIpAddressResourceId": { + "type": "string", + "metadata": { + "description": "Optional. The existing public IP address to assosciate with the NAT gateway. This should be provided if an existing public Ip address is available to be used. If this parameter is not provided, a new Public Ip address will be created." + } + }, + "computeNetworking": { + "$ref": "#/definitions/computeNetworkingType", + "metadata": { + "description": "Required. The compute type networking type." + } + } + } + }, + "containerAppNetworkConfigType": { + "type": "object", + "properties": { + "computeNetworkType": { + "type": "string", + "allowedValues": [ + "azureContainerApp" + ], + "metadata": { + "description": "Required. The Azure Container App networking type." + } + }, + "containerAppSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The existing network container app subnet name. This is required for Container Apps compute type. This subnet needs to have service delegation for App environments." + } + }, + "containerAppDeploymentScriptSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The existing subnet name for the container app deployment script." + } + }, + "deploymentScriptPrivateDnsZoneResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The deployment script private DNS zone Id. If not provided, a new private DNS zone will be created." + } + }, + "containerInstanceSubnetName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. The container instance subnet name in the created virtual network. If not provided, a default name will be used. This subnet is required for private networking Azure DevOps scenarios to deploy the deployment script which starts the placeholder agent privately." + } + } + } + }, + "containerInstanceNetworkConfigType": { + "type": "object", + "properties": { + "computeNetworkType": { + "type": "string", + "allowedValues": [ + "azureContainerInstance" + ], + "metadata": { + "description": "Required. The Azure Container Instance network type." + } + }, + "containerInstanceSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The container instance subnet name in the created virtual network. If not provided, a default name will be used." + } + } + } + }, + "networkType": { + "type": "object", + "discriminator": { + "propertyName": "networkType", + "mapping": { + "createNew": { + "$ref": "#/definitions/newNetworkType" + }, + "useExisting": { + "$ref": "#/definitions/existingNetworkType" + } + } + } + }, + "computeNetworkingType": { + "type": "object", + "discriminator": { + "propertyName": "computeNetworkType", + "mapping": { + "azureContainerApp": { + "$ref": "#/definitions/containerAppNetworkConfigType" + }, + "azureContainerInstance": { + "$ref": "#/definitions/containerInstanceNetworkConfigType" + } + } + } + }, + "azureContainerInstanceTargetType": { + "type": "object", + "properties": { + "sku": { + "type": "string", + "allowedValues": [ + "Dedicated", + "Standard" + ], + "nullable": true, + "metadata": { + "description": "Optional. The Azure Container Instance Sku name." + } + }, + "numberOfInstances": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The number of the Azure Container Instances to deploy." + } + }, + "cpu": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Container Instance container cpu." + } + }, + "memoryInGB": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Container Instance container memory." + } + }, + "port": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Container Instance container port." + } + } + }, + "nullable": true + }, + "azureContainerAppTargetType": { + "type": "object", + "properties": { + "resources": { + "$ref": "#/definitions/acaResourcesType", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Container App Job CPU and memory resources." + } + } + } + }, + "gitHubRunnersType": { + "type": "object", + "properties": { + "selfHostedType": { + "type": "string", + "allowedValues": [ + "github" + ], + "metadata": { + "description": "Required. The self-hosted runner type." + } + }, + "personalAccessToken": { + "type": "securestring", + "metadata": { + "description": "Required. The GitHub personal access token with permissions to create and manage self-hosted runners. See https://learn.microsoft.com/azure/container-apps/tutorial-ci-cd-runners-jobs?tabs=bash&pivots=container-apps-jobs-self-hosted-ci-cd-github-actions#get-a-github-personal-access-token for PAT permissions." + } + }, + "githubOrganization": { + "type": "string", + "metadata": { + "description": "Required. The GitHub organization name." + } + }, + "githubRepository": { + "type": "string", + "metadata": { + "description": "Required. The GitHub repository name." + } + }, + "runnerName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The GitHub runner name." + } + }, + "runnerGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The GitHub runner group." + } + }, + "runnerNamePrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The GitHub runner name prefix." + } + }, + "runnerScope": { + "type": "string", + "allowedValues": [ + "ent", + "org", + "repo" + ], + "nullable": true, + "metadata": { + "description": "Optional. The GitHub runner scope." + } + }, + "ephemeral": { + "type": "bool", + "allowedValues": [ + true + ], + "nullable": true, + "metadata": { + "description": "Optional. Deploy ephemeral runners." + } + }, + "targetWorkflowQueueLength": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The target workflow queue length." + } + }, + "azureContainerInstanceTarget": { + "$ref": "#/definitions/azureContainerInstanceTargetType", + "nullable": true, + "metadata": { + "description": "Optional. The GitHub runner Azure Container instance configuration." + } + }, + "azureContainerAppTarget": { + "$ref": "#/definitions/azureContainerAppTargetType", + "nullable": true, + "metadata": { + "description": "Optional. The GitHub runner Azure Container app configuration." + } + } + } + }, + "devOpsAgentsType": { + "type": "object", + "properties": { + "selfHostedType": { + "type": "string", + "allowedValues": [ + "azuredevops" + ], + "metadata": { + "description": "Required. The self-hosted runner type." + } + }, + "personalAccessToken": { + "type": "securestring", + "metadata": { + "description": "Required. The Azure DevOps persoanl access token with permissions to create and manage self-hosted agents. See https://learn.microsoft.com/azure/container-apps/tutorial-ci-cd-runners-jobs?tabs=bash&pivots=container-apps-jobs-self-hosted-ci-cd-azure-pipelines#get-an-azure-devops-personal-access-token for PAT permissions." + } + }, + "devOpsOrganization": { + "type": "string", + "metadata": { + "description": "Required. The Azure DevOps organization name." + } + }, + "agentsPoolName": { + "type": "string", + "metadata": { + "description": "Required. The Azure DevOps agents pool name." + } + }, + "agentName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Azure DevOps agent name." + } + }, + "agentNamePrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Azure DevOps agent name prefix." + } + }, + "placeHolderAgentName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Azure DevOps placeholder agent name." + } + }, + "targetPipelinesQueueLength": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The target pipelines queue length." + } + }, + "azureContainerInstanceTarget": { + "$ref": "#/definitions/azureContainerInstanceTargetType", + "nullable": true, + "metadata": { + "description": "Optional. The GitHub runner Azure Container instance configuration." + } + }, + "azureContainerAppTarget": { + "$ref": "#/definitions/azureContainerAppTargetType", + "nullable": true, + "metadata": { + "description": "Optional. The AzureDevOps agents Azure Container app configuration." + } + } + } + }, + "acaResourcesType": { + "type": "object", + "allowedValues": [ + { + "cpu": "0.25", + "memory": "0.5Gi" + }, + { + "cpu": "0.5", + "memory": "1Gi" + }, + { + "cpu": "0.75", + "memory": "1.5Gi" + }, + { + "cpu": "1", + "memory": "2Gi" + }, + { + "cpu": "1.25", + "memory": "2.5Gi" + }, + { + "cpu": "1.5", + "memory": "3Gi" + }, + { + "cpu": "1.75", + "memory": "3.5Gi" + }, + { + "cpu": "2", + "memory": "4Gi" + }, + { + "cpu": "2.25", + "memory": "4.5Gi" + }, + { + "cpu": "2.5", + "memory": "5Gi" + }, + { + "cpu": "2.75", + "memory": "5.5Gi" + }, + { + "cpu": "3", + "memory": "6Gi" + }, + { + "cpu": "3.25", + "memory": "6.5Gi" + }, + { + "cpu": "3.5", + "memory": "7Gi" + }, + { + "cpu": "3.75", + "memory": "7.5Gi" + }, + { + "cpu": "4", + "memory": "8Gi" + } + ] + }, + "selfHostedRunnerType": { + "type": "object", + "discriminator": { + "propertyName": "selfHostedType", + "mapping": { + "github": { + "$ref": "#/definitions/gitHubRunnersType" + }, + "azuredevops": { + "$ref": "#/definitions/devOpsAgentsType" + } + } + } + }, + "computeTypesType": { + "type": "array", + "allowedValues": [ + "azure-container-app", + "azure-container-instance" + ], + "metadata": { + "description": "Required. The target compute environments for the private runners." + } + } + }, + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "namingPrefix": { + "type": "string", + "metadata": { + "description": "Required. Naming prefix to be used with naming the deployed resources." + } + }, + "computeTypes": { + "$ref": "#/definitions/computeTypesType", + "metadata": { + "description": "Required. The compute target for the private runners." + } + }, + "selfHostedConfig": { + "$ref": "#/definitions/selfHostedRunnerType", + "metadata": { + "description": "Required. The self-hosted runner configuration. This can be either GitHub or Azure DevOps." + } + }, + "networkingConfiguration": { + "$ref": "#/definitions/networkType", + "metadata": { + "description": "Required. The networking configuration." + } + }, + "privateNetworking": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Whether to use private or public networking for the Azure Container Registry." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "$fxv#0": "<#\nThis scripts starts an Azure DevOps container job with a manual trigger. It is used to manually start the placeholder agent.\nMore Information can be found here https://learn.microsoft.com/azure/container-apps/tutorial-ci-cd-runners-jobs?tabs=bash&pivots=container-apps-jobs-self-hosted-ci-cd-azure-pipelines#create-a-placeholder-self-hosted-agent\n#>\n\nParam(\n [string]$jobName,\n [string]$resourceGroup,\n [string]$subscriptionId\n)\n$ErrorActionPreference = 'SilentlyContinue'\n$DeploymentScriptOutputs = @{}\n$maxRetries = 10\n$retryCount = 0\n\nif (Get-AzContainerAppJob -Name $jobName -ResourceGroupName $resourceGroup -SubscriptionId $subscriptionId -ErrorVariable jobStartError) {\n do {\n $jobStatus = (Get-AzContainerAppJob -Name $jobName -ResourceGroupName $resourceGroup -SubscriptionId $subscriptionId).ProvisioningState\n Write-Host 'Waiting for the container app job to start...Waiting 10 seconds!'\n Start-Sleep -Seconds 10\n $retryCount++\n } until ($jobStatus -eq 'Succeeded' -or $maxRetries -eq $retryCount)\n\n $jobStart = Start-AzContainerAppJob -Name $jobName -ResourceGroupName $resourceGroup -SubscriptionId $subscriptionId -ErrorVariable jobStartError\n if ($jobStart) {\n Write-Host 'Job started successfully.'\n $DeploymentScriptOutputs['JobStatus'] = 'Success'\n } else {\n Write-Host \"Failed to start Job. An error occurred: $($jobStartError[0].Exception.Message)\"\n $DeploymentScriptOutputs['JobStatus'] = 'Failed'\n }\n} else {\n Write-Host \"Job not found. $($jobStartError[0].Exception.Message)\"\n $DeploymentScriptOutputs['JobStatus'] = 'Failed'\n}\n\n", + "imagePaths": [ + { + "platform": "azuredevops-container-app", + "imagePath": "azure-devops-agent-aca" + }, + { + "platform": "github-container-app", + "imagePath": "github-runner-aca" + }, + { + "platform": "azuredevops-container-instance", + "imagePath": "azure-devops-agent-aci" + }, + { + "platform": "github-container-instance", + "imagePath": "github-runner-aci" + } + ], + "githubURL": "https://github.com/azure/bicep-registry-modules#main:avm/ptn/dev-ops/cicd-agents-and-runners/scripts", + "acaGitHubRules": "[if(equals(parameters('selfHostedConfig').selfHostedType, 'github'), createArray(createObject('name', 'github-runner-scaling-rule', 'type', 'github-runner', 'metadata', createObject('owner', parameters('selfHostedConfig').githubOrganization, 'repos', parameters('selfHostedConfig').githubRepository, 'targetWorkflowQueueLength', coalesce(tryGet(parameters('selfHostedConfig'), 'targetWorkflowQueueLength'), '1'), 'runnerScope', coalesce(tryGet(parameters('selfHostedConfig'), 'runnerScope'), 'repo')), 'auth', createArray(createObject('secretRef', 'personal-access-token', 'triggerParameter', 'personalAccessToken')))), createArray())]", + "acaGitHubSecrets": "[if(equals(parameters('selfHostedConfig').selfHostedType, 'github'), createArray(createObject('name', 'personal-access-token', 'value', parameters('selfHostedConfig').personalAccessToken)), createArray())]", + "acaGitHubEnvVariables": "[if(equals(parameters('selfHostedConfig').selfHostedType, 'github'), createArray(createObject('name', 'RUNNER_NAME_PREFIX', 'value', coalesce(tryGet(parameters('selfHostedConfig'), 'runnerNamePrefix'), 'gh-runner')), createObject('name', 'REPO_URL', 'value', variables('gitHubRunnerURL')), createObject('name', 'RUNNER_SCOPE', 'value', coalesce(tryGet(parameters('selfHostedConfig'), 'runnerScope'), 'repo')), createObject('name', 'EPHEMERAL', 'value', coalesce(tryGet(parameters('selfHostedConfig'), 'ephemeral'), 'true')), createObject('name', 'ORG_NAME', 'value', tryGet(parameters('selfHostedConfig'), 'gitHubOrganization')), createObject('name', 'RUNNER_GROUP', 'value', coalesce(tryGet(parameters('selfHostedConfig'), 'runnerGroup'), '')), createObject('name', 'ACCESS_TOKEN', 'secretRef', 'personal-access-token')), createArray())]", + "acaAzureDevOpsEnvVariables": "[if(equals(parameters('selfHostedConfig').selfHostedType, 'azuredevops'), createArray(createObject('name', 'AZP_POOL', 'value', parameters('selfHostedConfig').agentsPoolName), createObject('name', 'AZP_AGENT_NAME_PREFIX', 'value', coalesce(tryGet(parameters('selfHostedConfig'), 'agentNamePrefix'), 'aca')), createObject('name', 'AZP_URL', 'secretRef', 'organization-url'), createObject('name', 'AZP_TOKEN', 'secretRef', 'personal-access-token')), createArray())]", + "acaAzureDevOpsSecrets": "[if(equals(parameters('selfHostedConfig').selfHostedType, 'azuredevops'), createArray(createObject('name', 'personal-access-token', 'value', parameters('selfHostedConfig').personalAccessToken), createObject('name', 'organization-url', 'value', variables('devOpsOrgURL'))), createArray())]", + "acaAzureDevOpsRules": "[if(equals(parameters('selfHostedConfig').selfHostedType, 'azuredevops'), createArray(createObject('name', 'azure-pipelines-scaling-rule', 'type', 'azure-pipelines', 'metadata', createObject('poolName', parameters('selfHostedConfig').agentsPoolName, 'targetPipelinesQueueLength', coalesce(tryGet(parameters('selfHostedConfig'), 'targetPipelinesQueueLength'), '1')), 'auth', createArray(createObject('secretRef', 'personal-access-token', 'triggerParameter', 'personalAccessToken'), createObject('secretRef', 'organization-url', 'triggerParameter', 'organizationURL')))), createArray())]", + "gitHubRunnerURL": "[format('https://github.com/{0}/{1}', tryGet(parameters('selfHostedConfig'), 'gitHubOrganization'), tryGet(parameters('selfHostedConfig'), 'gitHubRepository'))]", + "devOpsOrgURL": "[format('https://dev.azure.com/{0}', tryGet(parameters('selfHostedConfig'), 'devOpsOrganization'))]" + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.ptn.devops-cicdagentsandrunners.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "buildImages": { + "copy": { + "name": "buildImages", + "count": "[length(parameters('computeTypes'))]" + }, + "type": "Microsoft.ContainerRegistry/registries/tasks", + "apiVersion": "2019-06-01-preview", + "name": "[format('{0}/buildImage-{1}-{2}-{3}', format('acr{0}{1}', parameters('namingPrefix'), uniqueString(resourceGroup().id)), parameters('computeTypes')[copyIndex()], parameters('selfHostedConfig').selfHostedType, copyIndex())]", + "location": "[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "platform": { + "os": "Linux" + }, + "step": { + "dockerFilePath": "dockerfile", + "type": "Docker", + "contextPath": "[if(contains(parameters('computeTypes'), 'azure-container-app'), format('{0}/{1}', variables('githubURL'), filter(variables('imagePaths'), lambda('imagePath', equals(lambdaVariables('imagePath').platform, format('{0}-container-app', parameters('selfHostedConfig').selfHostedType))))[0].imagePath), if(contains(parameters('computeTypes'), 'azure-container-instance'), format('{0}/{1}', variables('githubURL'), filter(variables('imagePaths'), lambda('imagePath', equals(lambdaVariables('imagePath').platform, format('{0}-container-instance', parameters('selfHostedConfig').selfHostedType))))[0].imagePath), null()))]", + "imageNames": [ + "[format('{0}/{1}-{2}:latest', reference('acr').outputs.loginServer.value, parameters('selfHostedConfig').selfHostedType, parameters('computeTypes')[copyIndex()])]" + ] + }, + "credentials": "[if(parameters('privateNetworking'), createObject('customRegistries', createObject(format('{0}', reference('acr').outputs.loginServer.value), createObject('identity', '[system]'))), null())]" + }, + "dependsOn": [ + "acr" + ] + }, + "taskRun": { + "copy": { + "name": "taskRun", + "count": "[length(parameters('computeTypes'))]" + }, + "type": "Microsoft.ContainerRegistry/registries/taskRuns", + "apiVersion": "2019-06-01-preview", + "name": "[format('{0}/taskrun-{1}-{2}-{3}', format('acr{0}{1}', parameters('namingPrefix'), uniqueString(resourceGroup().id)), parameters('computeTypes')[copyIndex()], parameters('selfHostedConfig').selfHostedType, copyIndex())]", + "location": "[parameters('location')]", + "properties": { + "runRequest": { + "taskId": "[resourceId('Microsoft.ContainerRegistry/registries/tasks', split(format('{0}/buildImage-{1}-{2}-{3}', format('acr{0}{1}', parameters('namingPrefix'), uniqueString(resourceGroup().id)), parameters('computeTypes')[copyIndex()], parameters('selfHostedConfig').selfHostedType, copyIndex()), '/')[0], split(format('{0}/buildImage-{1}-{2}-{3}', format('acr{0}{1}', parameters('namingPrefix'), uniqueString(resourceGroup().id)), parameters('computeTypes')[copyIndex()], parameters('selfHostedConfig').selfHostedType, copyIndex()), '/')[1])]", + "type": "TaskRunRequest" + } + }, + "dependsOn": [ + "acr", + "[format('buildImages[{0}]', copyIndex())]", + "buildImagesRoleAssignment" + ] + }, + "logAnalyticsWokrspace": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "logAnalyticsWokrspace", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('law-{0}-{1}-law', parameters('namingPrefix'), uniqueString(resourceGroup().id))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "6423883681003873806" + }, + "name": "Log Analytics Workspaces", + "description": "This module deploys a Log Analytics Workspace.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Log Analytics workspace." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "skuName": { + "type": "string", + "defaultValue": "PerGB2018", + "allowedValues": [ + "CapacityReservation", + "Free", + "LACluster", + "PerGB2018", + "PerNode", + "Premium", + "Standalone", + "Standard" + ], + "metadata": { + "description": "Optional. The name of the SKU." + } + }, + "skuCapacityReservationLevel": { + "type": "int", + "defaultValue": 100, + "minValue": 100, + "maxValue": 5000, + "metadata": { + "description": "Optional. The capacity reservation level in GB for this workspace, when CapacityReservation sku is selected. Must be in increments of 100 between 100 and 5000." + } + }, + "storageInsightsConfigs": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of storage accounts to be read by the workspace." + } + }, + "linkedServices": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of services to be linked." + } + }, + "linkedStorageAccounts": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Conditional. List of Storage Accounts to be linked. Required if 'forceCmkForQuery' is set to 'true' and 'savedSearches' is not empty." + } + }, + "savedSearches": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Kusto Query Language searches to save." + } + }, + "dataExports": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. LAW data export instances to be deployed." + } + }, + "dataSources": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. LAW data sources to configure." + } + }, + "tables": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. LAW custom tables to be deployed." + } + }, + "gallerySolutions": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of gallerySolutions to be created in the log analytics workspace." + } + }, + "dataRetention": { + "type": "int", + "defaultValue": 365, + "minValue": 0, + "maxValue": 730, + "metadata": { + "description": "Optional. Number of days data will be retained for." + } + }, + "dailyQuotaGb": { + "type": "int", + "defaultValue": -1, + "minValue": -1, + "metadata": { + "description": "Optional. The workspace daily quota for ingestion." + } + }, + "publicNetworkAccessForIngestion": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The network access type for accessing Log Analytics ingestion." + } + }, + "publicNetworkAccessForQuery": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The network access type for accessing Log Analytics query." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource. Only one type of identity is supported: system-assigned or user-assigned, but not both." + } + }, + "useResourcePermissions": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Set to 'true' to use resource or workspace permissions and 'false' (or leave empty) to require workspace permissions." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "useDeployedWorkspaceForDiagnosticSettings": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Instead of using an external reference, use the deployed instance as the target for its diagnostic settings." + } + }, + "forceCmkForQuery": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether customer managed storage is mandatory for query management." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), 'SystemAssigned', if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Log Analytics Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]", + "Log Analytics Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893')]", + "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')]", + "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')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.operationalinsights-workspace.{0}.{1}', replace('0.5.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "logAnalyticsWorkspace": { + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2022-10-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "features": { + "searchVersion": 1, + "enableLogAccessUsingOnlyResourcePermissions": "[parameters('useResourcePermissions')]" + }, + "sku": { + "name": "[parameters('skuName')]", + "capacityReservationLevel": "[if(equals(parameters('skuName'), 'CapacityReservation'), parameters('skuCapacityReservationLevel'), null())]" + }, + "retentionInDays": "[parameters('dataRetention')]", + "workspaceCapping": { + "dailyQuotaGb": "[parameters('dailyQuotaGb')]" + }, + "publicNetworkAccessForIngestion": "[parameters('publicNetworkAccessForIngestion')]", + "publicNetworkAccessForQuery": "[parameters('publicNetworkAccessForQuery')]", + "forceCmkForQuery": "[parameters('forceCmkForQuery')]" + }, + "identity": "[variables('identity')]" + }, + "logAnalyticsWorkspace_diagnosticSettings": { + "copy": { + "name": "logAnalyticsWorkspace_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.OperationalInsights/workspaces/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[if(parameters('useDeployedWorkspaceForDiagnosticSettings'), resourceId('Microsoft.OperationalInsights/workspaces', parameters('name')), tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId'))]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.OperationalInsights/workspaces/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_roleAssignments": { + "copy": { + "name": "logAnalyticsWorkspace_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.OperationalInsights/workspaces/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.OperationalInsights/workspaces', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_storageInsightConfigs": { + "copy": { + "name": "logAnalyticsWorkspace_storageInsightConfigs", + "count": "[length(parameters('storageInsightsConfigs'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-LAW-StorageInsightsConfig-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "logAnalyticsWorkspaceName": { + "value": "[parameters('name')]" + }, + "containers": { + "value": "[tryGet(parameters('storageInsightsConfigs')[copyIndex()], 'containers')]" + }, + "tables": { + "value": "[tryGet(parameters('storageInsightsConfigs')[copyIndex()], 'tables')]" + }, + "storageAccountResourceId": { + "value": "[parameters('storageInsightsConfigs')[copyIndex()].storageAccountResourceId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "1745671120474305926" + }, + "name": "Log Analytics Workspace Storage Insight Configs", + "description": "This module deploys a Log Analytics Workspace Storage Insight Config.", + "owner": "Azure/module-maintainers" + }, + "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", + "defaultValue": "[format('{0}-stinsconfig', last(split(parameters('storageAccountResourceId'), '/')))]", + "metadata": { + "description": "Optional. The name of the storage insights config." + } + }, + "storageAccountResourceId": { + "type": "string", + "metadata": { + "description": "Required. The Azure Resource Manager ID of the storage account resource." + } + }, + "containers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The names of the blob containers that the workspace should read." + } + }, + "tables": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The names of the Azure tables that the workspace should read." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to configure in the resource." + } + } + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[last(split(parameters('storageAccountResourceId'), '/'))]" + }, + "workspace": { + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2022-10-01", + "name": "[parameters('logAnalyticsWorkspaceName')]" + }, + "storageinsightconfig": { + "type": "Microsoft.OperationalInsights/workspaces/storageInsightConfigs", + "apiVersion": "2020-08-01", + "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", + "tags": "[parameters('tags')]", + "properties": { + "containers": "[parameters('containers')]", + "tables": "[parameters('tables')]", + "storageAccount": { + "id": "[parameters('storageAccountResourceId')]", + "key": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', last(split(parameters('storageAccountResourceId'), '/'))), '2022-09-01').keys[0].value]" + } + }, + "dependsOn": [ + "storageAccount", + "workspace" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed storage insights configuration." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces/storageInsightConfigs', parameters('logAnalyticsWorkspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group where the storage insight configuration is deployed." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the storage insights configuration." + }, + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_linkedServices": { + "copy": { + "name": "logAnalyticsWorkspace_linkedServices", + "count": "[length(parameters('linkedServices'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-LAW-LinkedService-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "logAnalyticsWorkspaceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('linkedServices')[copyIndex()].name]" + }, + "resourceId": { + "value": "[tryGet(parameters('linkedServices')[copyIndex()], 'resourceId')]" + }, + "writeAccessResourceId": { + "value": "[tryGet(parameters('linkedServices')[copyIndex()], 'writeAccessResourceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "12032441371027552374" + }, + "name": "Log Analytics Workspace Linked Services", + "description": "This module deploys a Log Analytics Workspace Linked Service.", + "owner": "Azure/module-maintainers" + }, + "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", + "nullable": true, + "metadata": { + "description": "Optional. Tags to configure in the resource." + } + } + }, + "resources": { + "workspace": { + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2022-10-01", + "name": "[parameters('logAnalyticsWorkspaceName')]" + }, + "linkedService": { + "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'))]" + }, + "dependsOn": [ + "workspace" + ] + } + }, + "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": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_linkedStorageAccounts": { + "copy": { + "name": "logAnalyticsWorkspace_linkedStorageAccounts", + "count": "[length(parameters('linkedStorageAccounts'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-LAW-LinkedStorageAccount-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "logAnalyticsWorkspaceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('linkedStorageAccounts')[copyIndex()].name]" + }, + "resourceId": { + "value": "[parameters('linkedStorageAccounts')[copyIndex()].resourceId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "12623216644328477682" + }, + "name": "Log Analytics Workspace Linked Storage Accounts", + "description": "This module deploys a Log Analytics Workspace Linked Storage Account.", + "owner": "Azure/module-maintainers" + }, + "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", + "allowedValues": [ + "Query", + "Alerts", + "CustomLogs", + "AzureWatson" + ], + "metadata": { + "description": "Required. Name of the link." + } + }, + "resourceId": { + "type": "string", + "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." + } + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces/linkedStorageAccounts", + "apiVersion": "2020-08-01", + "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", + "properties": { + "storageAccountIds": [ + "[parameters('resourceId')]" + ] + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed linked storage account." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed linked storage account." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces/linkedStorageAccounts', parameters('logAnalyticsWorkspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group where the linked storage account is deployed." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_savedSearches": { + "copy": { + "name": "logAnalyticsWorkspace_savedSearches", + "count": "[length(parameters('savedSearches'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-LAW-SavedSearch-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "logAnalyticsWorkspaceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[format('{0}{1}', parameters('savedSearches')[copyIndex()].name, uniqueString(deployment().name))]" + }, + "etag": { + "value": "[tryGet(parameters('savedSearches')[copyIndex()], 'etag')]" + }, + "displayName": { + "value": "[parameters('savedSearches')[copyIndex()].displayName]" + }, + "category": { + "value": "[parameters('savedSearches')[copyIndex()].category]" + }, + "query": { + "value": "[parameters('savedSearches')[copyIndex()].query]" + }, + "functionAlias": { + "value": "[tryGet(parameters('savedSearches')[copyIndex()], 'functionAlias')]" + }, + "functionParameters": { + "value": "[tryGet(parameters('savedSearches')[copyIndex()], 'functionParameters')]" + }, + "version": { + "value": "[tryGet(parameters('savedSearches')[copyIndex()], 'version')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "7683333179440464721" + }, + "name": "Log Analytics Workspace Saved Searches", + "description": "This module deploys a Log Analytics Workspace Saved Search.", + "owner": "Azure/module-maintainers" + }, + "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 saved search." + } + }, + "displayName": { + "type": "string", + "metadata": { + "description": "Required. Display name for the search." + } + }, + "category": { + "type": "string", + "metadata": { + "description": "Required. Query category." + } + }, + "query": { + "type": "string", + "metadata": { + "description": "Required. Kusto Query to be stored." + } + }, + "tags": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Tags to configure in the resource." + } + }, + "functionAlias": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The function alias if query serves as a function." + } + }, + "functionParameters": { + "type": "string", + "defaultValue": "", + "metadata": { + "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." + } + }, + "version": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The version number of the query language." + } + }, + "etag": { + "type": "string", + "defaultValue": "*", + "metadata": { + "description": "Optional. The ETag of the saved search. To override an existing saved search, use \"*\" or specify the current Etag." + } + } + }, + "resources": { + "workspace": { + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2022-10-01", + "name": "[parameters('logAnalyticsWorkspaceName')]" + }, + "savedSearch": { + "type": "Microsoft.OperationalInsights/workspaces/savedSearches", + "apiVersion": "2020-08-01", + "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", + "properties": { + "etag": "[parameters('etag')]", + "tags": "[coalesce(parameters('tags'), createArray())]", + "displayName": "[parameters('displayName')]", + "category": "[parameters('category')]", + "query": "[parameters('query')]", + "functionAlias": "[parameters('functionAlias')]", + "functionParameters": "[parameters('functionParameters')]", + "version": "[parameters('version')]" + }, + "dependsOn": [ + "workspace" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed saved search." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces/savedSearches', parameters('logAnalyticsWorkspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group where the saved search is deployed." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed saved search." + }, + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace", + "logAnalyticsWorkspace_linkedStorageAccounts" + ] + }, + "logAnalyticsWorkspace_dataExports": { + "copy": { + "name": "logAnalyticsWorkspace_dataExports", + "count": "[length(parameters('dataExports'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-LAW-DataExport-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "workspaceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('dataExports')[copyIndex()].name]" + }, + "destination": { + "value": "[tryGet(parameters('dataExports')[copyIndex()], 'destination')]" + }, + "enable": { + "value": "[tryGet(parameters('dataExports')[copyIndex()], 'enable')]" + }, + "tableNames": { + "value": "[tryGet(parameters('dataExports')[copyIndex()], 'tableNames')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5765609820817623497" + }, + "name": "Log Analytics Workspace Data Exports", + "description": "This module deploys a Log Analytics Workspace Data Export.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "minLength": 4, + "maxLength": 63, + "metadata": { + "description": "Required. The data export rule name." + } + }, + "workspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent workspaces. Required if the template is used in a standalone deployment." + } + }, + "destination": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Destination properties." + } + }, + "enable": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Active when enabled." + } + }, + "tableNames": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of tables to export, for example: ['Heartbeat', 'SecurityEvent']." + } + } + }, + "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces/dataExports", + "apiVersion": "2020-08-01", + "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('name'))]", + "properties": { + "destination": "[parameters('destination')]", + "enable": "[parameters('enable')]", + "tableNames": "[parameters('tableNames')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the data export." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the data export." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces/dataExports', parameters('workspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the data export was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_dataSources": { + "copy": { + "name": "logAnalyticsWorkspace_dataSources", + "count": "[length(parameters('dataSources'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-LAW-DataSource-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "logAnalyticsWorkspaceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('dataSources')[copyIndex()].name]" + }, + "kind": { + "value": "[parameters('dataSources')[copyIndex()].kind]" + }, + "linkedResourceId": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'linkedResourceId')]" + }, + "eventLogName": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'eventLogName')]" + }, + "eventTypes": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'eventTypes')]" + }, + "objectName": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'objectName')]" + }, + "instanceName": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'instanceName')]" + }, + "intervalSeconds": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'intervalSeconds')]" + }, + "counterName": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'counterName')]" + }, + "state": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'state')]" + }, + "syslogName": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'syslogName')]" + }, + "syslogSeverities": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'syslogSeverities')]" + }, + "performanceCounters": { + "value": "[tryGet(parameters('dataSources')[copyIndex()], 'performanceCounters')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "13460038983765020046" + }, + "name": "Log Analytics Workspace Datasources", + "description": "This module deploys a Log Analytics Workspace Data Source.", + "owner": "Azure/module-maintainers" + }, + "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 solution." + } + }, + "kind": { + "type": "string", + "defaultValue": "AzureActivityLog", + "allowedValues": [ + "AzureActivityLog", + "WindowsEvent", + "WindowsPerformanceCounter", + "IISLogs", + "LinuxSyslog", + "LinuxSyslogCollection", + "LinuxPerformanceObject", + "LinuxPerformanceCollection" + ], + "metadata": { + "description": "Required. The kind of the DataSource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to configure in the resource." + } + }, + "linkedResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the resource to be linked." + } + }, + "eventLogName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Windows event log name to configure when kind is WindowsEvent." + } + }, + "eventTypes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Windows event types to configure when kind is WindowsEvent." + } + }, + "objectName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Name of the object to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject." + } + }, + "instanceName": { + "type": "string", + "defaultValue": "*", + "metadata": { + "description": "Optional. Name of the instance to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject." + } + }, + "intervalSeconds": { + "type": "int", + "defaultValue": 60, + "metadata": { + "description": "Optional. Interval in seconds to configure when kind is WindowsPerformanceCounter or LinuxPerformanceObject." + } + }, + "performanceCounters": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of counters to configure when the kind is LinuxPerformanceObject." + } + }, + "counterName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Counter name to configure when kind is WindowsPerformanceCounter." + } + }, + "state": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. State to configure when kind is IISLogs or LinuxSyslogCollection or LinuxPerformanceCollection." + } + }, + "syslogName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. System log to configure when kind is LinuxSyslog." + } + }, + "syslogSeverities": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Severities to configure when kind is LinuxSyslog." + } + } + }, + "resources": { + "workspace": { + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2022-10-01", + "name": "[parameters('logAnalyticsWorkspaceName')]" + }, + "dataSource": { + "type": "Microsoft.OperationalInsights/workspaces/dataSources", + "apiVersion": "2020-08-01", + "name": "[format('{0}/{1}', parameters('logAnalyticsWorkspaceName'), parameters('name'))]", + "kind": "[parameters('kind')]", + "tags": "[parameters('tags')]", + "properties": { + "linkedResourceId": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'AzureActivityLog')), parameters('linkedResourceId'), null())]", + "eventLogName": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'WindowsEvent')), parameters('eventLogName'), null())]", + "eventTypes": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'WindowsEvent')), parameters('eventTypes'), null())]", + "objectName": "[if(and(not(empty(parameters('kind'))), or(equals(parameters('kind'), 'WindowsPerformanceCounter'), equals(parameters('kind'), 'LinuxPerformanceObject'))), parameters('objectName'), null())]", + "instanceName": "[if(and(not(empty(parameters('kind'))), or(equals(parameters('kind'), 'WindowsPerformanceCounter'), equals(parameters('kind'), 'LinuxPerformanceObject'))), parameters('instanceName'), null())]", + "intervalSeconds": "[if(and(not(empty(parameters('kind'))), or(equals(parameters('kind'), 'WindowsPerformanceCounter'), equals(parameters('kind'), 'LinuxPerformanceObject'))), parameters('intervalSeconds'), null())]", + "counterName": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'WindowsPerformanceCounter')), parameters('counterName'), null())]", + "state": "[if(and(not(empty(parameters('kind'))), or(or(equals(parameters('kind'), 'IISLogs'), equals(parameters('kind'), 'LinuxSyslogCollection')), equals(parameters('kind'), 'LinuxPerformanceCollection'))), parameters('state'), null())]", + "syslogName": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'LinuxSyslog')), parameters('syslogName'), null())]", + "syslogSeverities": "[if(and(not(empty(parameters('kind'))), or(equals(parameters('kind'), 'LinuxSyslog'), equals(parameters('kind'), 'LinuxPerformanceObject'))), parameters('syslogSeverities'), null())]", + "performanceCounters": "[if(and(not(empty(parameters('kind'))), equals(parameters('kind'), 'LinuxPerformanceObject')), parameters('performanceCounters'), null())]" + }, + "dependsOn": [ + "workspace" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed data source." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces/dataSources', parameters('logAnalyticsWorkspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group where the data source is deployed." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed data source." + }, + "value": "[parameters('name')]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_tables": { + "copy": { + "name": "logAnalyticsWorkspace_tables", + "count": "[length(parameters('tables'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-LAW-Table-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "workspaceName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('tables')[copyIndex()].name]" + }, + "plan": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'plan')]" + }, + "schema": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'schema')]" + }, + "retentionInDays": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'retentionInDays')]" + }, + "totalRetentionInDays": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'totalRetentionInDays')]" + }, + "restoredLogs": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'restoredLogs')]" + }, + "searchResults": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'searchResults')]" + }, + "roleAssignments": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "6905244456918791391" + }, + "name": "Log Analytics Workspace Tables", + "description": "This module deploys a Log Analytics Workspace Table.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the table." + } + }, + "workspaceName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent workspaces. Required if the template is used in a standalone deployment." + } + }, + "plan": { + "type": "string", + "defaultValue": "Analytics", + "allowedValues": [ + "Basic", + "Analytics" + ], + "metadata": { + "description": "Optional. Instruct the system how to handle and charge the logs ingested to this table." + } + }, + "restoredLogs": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Restore parameters." + } + }, + "retentionInDays": { + "type": "int", + "defaultValue": -1, + "minValue": -1, + "maxValue": 730, + "metadata": { + "description": "Optional. The table retention in days, between 4 and 730. Setting this property to -1 will default to the workspace retention." + } + }, + "schema": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Table's schema." + } + }, + "searchResults": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Parameters of the search job that initiated this table." + } + }, + "totalRetentionInDays": { + "type": "int", + "defaultValue": -1, + "minValue": -1, + "maxValue": 2555, + "metadata": { + "description": "Optional. The table total retention in days, between 4 and 2555. Setting this property to -1 will default to table retention." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "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')]", + "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')]", + "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": { + "workspace": { + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2022-10-01", + "name": "[parameters('workspaceName')]" + }, + "table": { + "type": "Microsoft.OperationalInsights/workspaces/tables", + "apiVersion": "2022-10-01", + "name": "[format('{0}/{1}', parameters('workspaceName'), parameters('name'))]", + "properties": { + "plan": "[parameters('plan')]", + "restoredLogs": "[parameters('restoredLogs')]", + "retentionInDays": "[parameters('retentionInDays')]", + "schema": "[parameters('schema')]", + "searchResults": "[parameters('searchResults')]", + "totalRetentionInDays": "[parameters('totalRetentionInDays')]" + }, + "dependsOn": [ + "workspace" + ] + }, + "table_roleAssignments": { + "copy": { + "name": "table_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.OperationalInsights/workspaces/{0}/tables/{1}', parameters('workspaceName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.OperationalInsights/workspaces/tables', parameters('workspaceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "table" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the table." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the table." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces/tables', parameters('workspaceName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the table was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "logAnalyticsWorkspace_solutions": { + "copy": { + "name": "logAnalyticsWorkspace_solutions", + "count": "[length(parameters('gallerySolutions'))]" + }, + "condition": "[not(empty(parameters('gallerySolutions')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-LAW-Solution-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('gallerySolutions')[copyIndex()].name]" + }, + "location": { + "value": "[parameters('location')]" + }, + "logAnalyticsWorkspaceName": { + "value": "[parameters('name')]" + }, + "product": { + "value": "[tryGet(parameters('gallerySolutions')[copyIndex()], 'product')]" + }, + "publisher": { + "value": "[tryGet(parameters('gallerySolutions')[copyIndex()], 'publisher')]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(parameters('gallerySolutions')[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + } + }, + "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": "18444780972506374592" + }, + "name": "Operations Management Solutions", + "description": "This module deploys an Operations Management Solution.", + "owner": "Azure/module-maintainers" + }, + "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`." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "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('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.operationsmanagement-solution.{0}.{1}', replace('0.1.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + { + "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": [ + "logAnalyticsWorkspace" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed log analytics workspace." + }, + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed log analytics workspace." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed log analytics workspace." + }, + "value": "[parameters('name')]" + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "metadata": { + "description": "The ID associated with the workspace." + }, + "value": "[reference('logAnalyticsWorkspace').customerId]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('logAnalyticsWorkspace', '2022-10-01', 'full').location]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('logAnalyticsWorkspace', '2022-10-01', 'full'), 'identity'), 'principalId'), '')]" + } + } + } + } + }, + "userAssignedIdentity": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "userAssignedIdentity", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('msi-{0}-{1}', parameters('namingPrefix'), uniqueString(resourceGroup().id))]" + }, + "location": { + "value": "[parameters('location')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "10247609258451563080" + }, + "name": "User Assigned Identities", + "description": "This module deploys a User Assigned Identity.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "federatedIdentityCredentialsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the federated identity credential." + } + }, + "audiences": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of audiences that can appear in the issued token." + } + }, + "issuer": { + "type": "string", + "metadata": { + "description": "Required. The URL of the issuer to be trusted." + } + }, + "subject": { + "type": "string", + "metadata": { + "description": "Required. The identifier of the external identity." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the User Assigned Identity." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "federatedIdentityCredentials": { + "$ref": "#/definitions/federatedIdentityCredentialsType", + "metadata": { + "description": "Optional. The federated identity credentials list to indicate which token from the external IdP should be trusted by your application. Federated identity credentials are supported on applications only. A maximum of 20 federated identity credentials can be added per application object." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Managed Identity Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59')]", + "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.managedidentity-userassignedidentity.{0}.{1}', replace('0.3.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "userAssignedIdentity": { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "userAssignedIdentity_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.ManagedIdentity/userAssignedIdentities/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "userAssignedIdentity" + ] + }, + "userAssignedIdentity_roleAssignments": { + "copy": { + "name": "userAssignedIdentity_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ManagedIdentity/userAssignedIdentities/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "userAssignedIdentity" + ] + }, + "userAssignedIdentity_federatedIdentityCredentials": { + "copy": { + "name": "userAssignedIdentity_federatedIdentityCredentials", + "count": "[length(coalesce(parameters('federatedIdentityCredentials'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-UserMSI-FederatedIdentityCredential-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].name]" + }, + "userAssignedIdentityName": { + "value": "[parameters('name')]" + }, + "audiences": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].audiences]" + }, + "issuer": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].issuer]" + }, + "subject": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].subject]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "3716898257490923786" + }, + "name": "User Assigned Identity Federated Identity Credential", + "description": "This module deploys a User Assigned Identity Federated Identity Credential.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "userAssignedIdentityName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent user assigned identity. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." + } + }, + "audiences": { + "type": "array", + "metadata": { + "description": "Required. The list of audiences that can appear in the issued token. Should be set to api://AzureADTokenExchange for Azure AD. It says what Microsoft identity platform should accept in the aud claim in the incoming token. This value represents Azure AD in your external identity provider and has no fixed value across identity providers - you might need to create a new application registration in your IdP to serve as the audience of this token." + } + }, + "issuer": { + "type": "string", + "metadata": { + "description": "Required. The URL of the issuer to be trusted. Must match the issuer claim of the external token being exchanged." + } + }, + "subject": { + "type": "string", + "metadata": { + "description": "Required. The identifier of the external software workload within the external identity provider. Like the audience value, it has no fixed format, as each IdP uses their own - sometimes a GUID, sometimes a colon delimited identifier, sometimes arbitrary strings. The value here must match the sub claim within the token presented to Azure AD." + } + } + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials", + "apiVersion": "2023-01-31", + "name": "[format('{0}/{1}', parameters('userAssignedIdentityName'), parameters('name'))]", + "properties": { + "audiences": "[parameters('audiences')]", + "issuer": "[parameters('issuer')]", + "subject": "[parameters('subject')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the federated identity credential." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the federated identity credential." + }, + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials', parameters('userAssignedIdentityName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the federated identity credential was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "userAssignedIdentity" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the user assigned identity." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the user assigned identity." + }, + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name'))]" + }, + "principalId": { + "type": "string", + "metadata": { + "description": "The principal ID (object ID) of the user assigned identity." + }, + "value": "[reference('userAssignedIdentity').principalId]" + }, + "clientId": { + "type": "string", + "metadata": { + "description": "The client ID (application ID) of the user assigned identity." + }, + "value": "[reference('userAssignedIdentity').clientId]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the user assigned identity was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('userAssignedIdentity', '2023-01-31', 'full').location]" + } + } + } + } + }, + "acrPrivateDNSZone": { + "condition": "[and(parameters('privateNetworking'), empty(coalesce(tryGet(parameters('networkingConfiguration'), 'containerRegistryPrivateDnsZoneResourceId'), '')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('acrdnszone{0}{1}', parameters('namingPrefix'), uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "privatelink.azurecr.io" + }, + "virtualNetworkLinks": { + "value": [ + { + "virtualNetworkResourceId": "[if(equals(parameters('networkingConfiguration').networkType, 'createNew'), reference('newVnet').outputs.resourceId.value, parameters('networkingConfiguration').virtualNetworkResourceId)]" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "17411368014038876941" + }, + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "aType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv4Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv4 address of this A record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + } + } + }, + "nullable": true + }, + "aaaaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aaaaRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv6Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv6 address of this AAAA record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + } + } + }, + "nullable": true + }, + "cnameType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The CNAME record in the record set." + } + } + } + }, + "nullable": true + }, + "mxType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "mxRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." + } + }, + "preference": { + "type": "int", + "metadata": { + "description": "Required. The preference value for this MX record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + } + } + }, + "nullable": true + }, + "ptrType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "ptrRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + } + } + }, + "nullable": true + }, + "soaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { + "type": "object", + "properties": { + "email": { + "type": "string", + "metadata": { + "description": "Required. The email contact for this SOA record." + } + }, + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } + }, + "host": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the authoritative name server for this SOA record." + } + }, + "minimumTtl": { + "type": "int", + "metadata": { + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + } + }, + "refreshTime": { + "type": "int", + "metadata": { + "description": "Required. The refresh value for this SOA record." + } + }, + "retryTime": { + "type": "int", + "metadata": { + "description": "Required. The retry time for this SOA record." + } + }, + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." + } + } + } + }, + "nullable": true + }, + "srvType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "srvRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } + }, + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." + } + }, + "port": { + "type": "int", + "metadata": { + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + } + } + }, + "nullable": true + }, + "txtType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "txtRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The text value of this TXT record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + } + } + }, + "nullable": true + }, + "virtualNetworkLinkType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "minLength": 1, + "maxLength": 80, + "metadata": { + "description": "Optional. The resource name." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the virtual network to link." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Region where the resource lives." + } + }, + "registrationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Private DNS zone name." + } + }, + "a": { + "$ref": "#/definitions/aType", + "metadata": { + "description": "Optional. Array of A records." + } + }, + "aaaa": { + "$ref": "#/definitions/aaaaType", + "metadata": { + "description": "Optional. Array of AAAA records." + } + }, + "cname": { + "$ref": "#/definitions/cnameType", + "metadata": { + "description": "Optional. Array of CNAME records." + } + }, + "mx": { + "$ref": "#/definitions/mxType", + "metadata": { + "description": "Optional. Array of MX records." + } + }, + "ptr": { + "$ref": "#/definitions/ptrType", + "metadata": { + "description": "Optional. Array of PTR records." + } + }, + "soa": { + "$ref": "#/definitions/soaType", + "metadata": { + "description": "Optional. Array of SOA records." + } + }, + "srv": { + "$ref": "#/definitions/srvType", + "metadata": { + "description": "Optional. Array of SRV records." + } + }, + "txt": { + "$ref": "#/definitions/txtType", + "metadata": { + "description": "Optional. Array of TXT records." + } + }, + "virtualNetworkLinks": { + "$ref": "#/definitions/virtualNetworkLinkType", + "metadata": { + "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' 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." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.5.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateDnsZone": { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "privateDnsZone_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_roleAssignments": { + "copy": { + "name": "privateDnsZone_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_A": { + "copy": { + "name": "privateDnsZone_A", + "count": "[length(coalesce(parameters('a'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" + }, + "aRecords": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "11343565859504250241" + }, + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the A record." + } + }, + "aRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "A": { + "type": "Microsoft.Network/privateDnsZones/A", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "aRecords": "[parameters('aRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "A_roleAssignments": { + "copy": { + "name": "A_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "A" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed A record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed A record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed A record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_AAAA": { + "copy": { + "name": "privateDnsZone_AAAA", + "count": "[length(coalesce(parameters('aaaa'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" + }, + "aaaaRecords": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "3700934406905797316" + }, + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the AAAA record." + } + }, + "aaaaRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "AAAA": { + "type": "Microsoft.Network/privateDnsZones/AAAA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "aaaaRecords": "[parameters('aaaaRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "AAAA_roleAssignments": { + "copy": { + "name": "AAAA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "AAAA" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed AAAA record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed AAAA record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed AAAA record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_CNAME": { + "copy": { + "name": "privateDnsZone_CNAME", + "count": "[length(coalesce(parameters('cname'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" + }, + "cnameRecord": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "12929118683431932277" + }, + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the CNAME record." + } + }, + "cnameRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A CNAME record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "CNAME": { + "type": "Microsoft.Network/privateDnsZones/CNAME", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "cnameRecord": "[parameters('cnameRecord')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "CNAME_roleAssignments": { + "copy": { + "name": "CNAME_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "CNAME" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed CNAME record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed CNAME record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed CNAME record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_MX": { + "copy": { + "name": "privateDnsZone_MX", + "count": "[length(coalesce(parameters('mx'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" + }, + "mxRecords": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "14178508926823177155" + }, + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the MX record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "mxRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "MX": { + "type": "Microsoft.Network/privateDnsZones/MX", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "mxRecords": "[parameters('mxRecords')]", + "ttl": "[parameters('ttl')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "MX_roleAssignments": { + "copy": { + "name": "MX_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "MX" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed MX record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed MX record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed MX record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_PTR": { + "copy": { + "name": "privateDnsZone_PTR", + "count": "[length(coalesce(parameters('ptr'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" + }, + "ptrRecords": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "8082128465097964607" + }, + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the PTR record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ptrRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "PTR": { + "type": "Microsoft.Network/privateDnsZones/PTR", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "ptrRecords": "[parameters('ptrRecords')]", + "ttl": "[parameters('ttl')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "PTR_roleAssignments": { + "copy": { + "name": "PTR_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "PTR" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed PTR record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed PTR record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed PTR record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_SOA": { + "copy": { + "name": "privateDnsZone_SOA", + "count": "[length(coalesce(parameters('soa'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" + }, + "soaRecord": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "6755707328778392735" + }, + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SOA record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "soaRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A SOA record." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SOA": { + "type": "Microsoft.Network/privateDnsZones/SOA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "soaRecord": "[parameters('soaRecord')]", + "ttl": "[parameters('ttl')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "SOA_roleAssignments": { + "copy": { + "name": "SOA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "SOA" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SOA record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SOA record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SOA record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_SRV": { + "copy": { + "name": "privateDnsZone_SRV", + "count": "[length(coalesce(parameters('srv'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" + }, + "srvRecords": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "662436323695062210" + }, + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SRV record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "srvRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SRV": { + "type": "Microsoft.Network/privateDnsZones/SRV", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "srvRecords": "[parameters('srvRecords')]", + "ttl": "[parameters('ttl')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "SRV_roleAssignments": { + "copy": { + "name": "SRV_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "SRV" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SRV record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SRV record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SRV record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_TXT": { + "copy": { + "name": "privateDnsZone_TXT", + "count": "[length(coalesce(parameters('txt'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" + }, + "txtRecords": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "356727884506799436" + }, + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the TXT record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "txtRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "TXT": { + "type": "Microsoft.Network/privateDnsZones/TXT", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]", + "txtRecords": "[parameters('txtRecords')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "TXT_roleAssignments": { + "copy": { + "name": "TXT_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "TXT" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed TXT record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed TXT record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed TXT record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_virtualNetworkLinks": { + "copy": { + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" + }, + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + }, + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "1713449351614683457" + }, + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The name of the virtual network link." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "registrationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. Link to another virtual network resource ID." + } + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" + } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed virtual network link." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed virtual network link." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed virtual network link." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetworkLink', '2020-06-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private DNS zone was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private DNS zone." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private DNS zone." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "newVnet" + ] + }, + "acr": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('acr{0}{1}', parameters('namingPrefix'), uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('acr{0}{1}', parameters('namingPrefix'), uniqueString(resourceGroup().id))]" + }, + "acrSku": "[if(parameters('privateNetworking'), createObject('value', 'Premium'), createObject('value', 'Standard'))]", + "acrAdminUserEnabled": { + "value": false + }, + "roleAssignments": { + "value": [ + { + "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]", + "roleDefinitionIdOrName": "7f951dda-4ed3-4680-a7ca-43fe172d538d" + }, + { + "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]", + "roleDefinitionIdOrName": "8311e382-0749-4cb8-b61a-304f252e45ec" + } + ] + }, + "networkRuleBypassOptions": "[if(parameters('privateNetworking'), createObject('value', 'AzureServices'), createObject('value', 'None'))]", + "privateEndpoints": "[if(parameters('privateNetworking'), createObject('value', createArray(createObject('subnetResourceId', if(equals(parameters('networkingConfiguration').networkType, 'createNew'), filter(reference('newVnet').outputs.subnetResourceIds.value, lambda('subnetId', contains(lambdaVariables('subnetId'), coalesce(tryGet(parameters('networkingConfiguration'), 'containerRegistryPrivateEndpointSubnetName'), 'acr-subnet'))))[0], if(equals(parameters('networkingConfiguration').networkType, 'useExisting'), format('{0}/subnets/{1}', parameters('networkingConfiguration').virtualNetworkResourceId, parameters('networkingConfiguration').containerRegistryPrivateEndpointSubnetName), null())), 'privateDnsZoneResourceIds', if(not(empty(coalesce(tryGet(parameters('networkingConfiguration'), 'containerRegistryPrivateDnsZoneResourceId'), ''))), createArray(coalesce(tryGet(parameters('networkingConfiguration'), 'containerRegistryPrivateDnsZoneResourceId'), '')), createArray(if(empty(coalesce(tryGet(parameters('networkingConfiguration'), 'containerRegistryPrivateDnsZoneResourceId'), '')), reference('acrPrivateDNSZone').outputs.resourceId.value, ''))), 'privateDnsZoneGroupName', 'acrPrivateDNSZoneGroup'))), createObject('value', null()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "8799580877381308457" + }, + "name": "Azure Container Registries (ACR)", + "description": "This module deploys an Azure Container Registry (ACR).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "privateEndpointType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true + }, + "scopeMapsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the scope map." + } + }, + "actions": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of scoped permissions for registry artifacts." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The user friendly description of the scope map." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 5, + "maxLength": 50, + "metadata": { + "description": "Required. Name of your Azure Container Registry." + } + }, + "acrAdminUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable admin user that have push / pull permission to the registry." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "acrSku": { + "type": "string", + "defaultValue": "Basic", + "allowedValues": [ + "Basic", + "Premium", + "Standard" + ], + "metadata": { + "description": "Optional. Tier of your Azure container registry." + } + }, + "exportPolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the export policy is enabled or not." + } + }, + "quarantinePolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the quarantine policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "trustPolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the trust policy is enabled or not. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "retentionPolicyStatus": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the retention policy is enabled or not." + } + }, + "retentionPolicyDays": { + "type": "int", + "defaultValue": 15, + "metadata": { + "description": "Optional. The number of days to retain an untagged manifest after which it gets purged." + } + }, + "azureADAuthenticationAsArmPolicyStatus": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The value that indicates whether the policy for using ARM audience token for a container registr is enabled or not. Default is enabled." + } + }, + "softDeletePolicyStatus": { + "type": "string", + "defaultValue": "disabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. Soft Delete policy status. Default is disabled." + } + }, + "softDeletePolicyDays": { + "type": "int", + "defaultValue": 7, + "metadata": { + "description": "Optional. The number of days after which a soft-deleted item is permanently deleted." + } + }, + "dataEndpointEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable a single data endpoint per region for serving data. Not relevant in case of disabled public access. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "publicNetworkAccess": { + "type": "string", + "nullable": true, + "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 and networkRuleSetIpRules are not set. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "networkRuleBypassOptions": { + "type": "string", + "defaultValue": "AzureServices", + "allowedValues": [ + "AzureServices", + "None" + ], + "metadata": { + "description": "Optional. Whether to allow trusted Azure services to access a network restricted registry." + } + }, + "networkRuleSetDefaultAction": { + "type": "string", + "defaultValue": "Deny", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Optional. The default action of allow or deny when no other rules match." + } + }, + "networkRuleSetIpRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The IP ACL rules. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "privateEndpoints": { + "$ref": "#/definitions/privateEndpointType", + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. Note, requires the 'acrSku' to be 'Premium'." + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether or not zone redundancy is enabled for this container registry." + } + }, + "replications": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. All replications to create." + } + }, + "webhooks": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. All webhooks to create." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "anonymousPullEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables registry-wide pull from unauthenticated clients. It's in preview and available in the Standard and Premium service tiers." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "cacheRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Array of Cache Rules." + } + }, + "credentialSets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of Credential Sets." + } + }, + "scopeMaps": { + "$ref": "#/definitions/scopeMapsType", + "metadata": { + "description": "Optional. Scope maps setting." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "AcrDelete": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c2f4ef07-c644-48eb-af81-4b1b4947fb11')]", + "AcrImageSigner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '6cef56e8-d556-48e5-a04f-b8e64114680f')]", + "AcrPull": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]", + "AcrPush": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8311e382-0749-4cb8-b61a-304f252e45ec')]", + "AcrQuarantineReader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cdda3590-29a3-44f6-95f2-9f980659eb04')]", + "AcrQuarantineWriter": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c8d4ff99-41c3-41a8-9f60-21dfdad59608')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", + "dependsOn": [ + "cMKKeyVault" + ] + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.containerregistry-registry.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + }, + "registry": { + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('acrSku')]" + }, + "properties": { + "anonymousPullEnabled": "[parameters('anonymousPullEnabled')]", + "adminUserEnabled": "[parameters('acrAdminUserEnabled')]", + "encryption": "[if(not(empty(parameters('customerManagedKey'))), createObject('status', 'enabled', 'keyVaultProperties', createObject('identity', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), reference('cMKUserAssignedIdentity').clientId, null()), 'keyIdentifier', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), format('{0}/{1}', reference('cMKKeyVault::cMKKey').keyUri, parameters('customerManagedKey').keyVersion), reference('cMKKeyVault::cMKKey').keyUriWithVersion))), null())]", + "policies": { + "azureADAuthenticationAsArmPolicy": { + "status": "[parameters('azureADAuthenticationAsArmPolicyStatus')]" + }, + "exportPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('exportPolicyStatus')), null())]", + "quarantinePolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('status', parameters('quarantinePolicyStatus')), null())]", + "trustPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('type', 'Notary', 'status', parameters('trustPolicyStatus')), null())]", + "retentionPolicy": "[if(equals(parameters('acrSku'), 'Premium'), createObject('days', parameters('retentionPolicyDays'), 'status', parameters('retentionPolicyStatus')), null())]", + "softDeletePolicy": { + "retentionDays": "[parameters('softDeletePolicyDays')]", + "status": "[parameters('softDeletePolicyStatus')]" + } + }, + "dataEndpointEnabled": "[parameters('dataEndpointEnabled')]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkRuleSetIpRules'))), 'Disabled', null()))]", + "networkRuleBypassOptions": "[parameters('networkRuleBypassOptions')]", + "networkRuleSet": "[if(not(empty(parameters('networkRuleSetIpRules'))), createObject('defaultAction', parameters('networkRuleSetDefaultAction'), 'ipRules', parameters('networkRuleSetIpRules')), null())]", + "zoneRedundancy": "[if(equals(parameters('acrSku'), 'Premium'), parameters('zoneRedundancy'), null())]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKUserAssignedIdentity" + ] + }, + "registry_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "registry" + ] + }, + "registry_diagnosticSettings": { + "copy": { + "name": "registry_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "registry" + ] + }, + "registry_roleAssignments": { + "copy": { + "name": "registry_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ContainerRegistry/registries/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "registry" + ] + }, + "registry_scopeMaps": { + "copy": { + "name": "registry_scopeMaps", + "count": "[length(coalesce(parameters('scopeMaps'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Scope-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(coalesce(parameters('scopeMaps'), createArray())[copyIndex()], 'name')]" + }, + "actions": { + "value": "[coalesce(parameters('scopeMaps'), createArray())[copyIndex()].actions]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('scopeMaps'), createArray())[copyIndex()], 'description')]" + }, + "registryName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "9144531012597082524" + }, + "name": "Container Registries scopeMaps", + "description": "This module deploys an Azure Container Registry (ACR) scopeMap.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-scopemaps', parameters('registryName'))]", + "metadata": { + "description": "Optional. The name of the scope map." + } + }, + "actions": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of scoped permissions for registry artifacts." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The user friendly description of the scope map." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "scopeMap": { + "type": "Microsoft.ContainerRegistry/registries/scopeMaps", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "properties": { + "actions": "[parameters('actions')]", + "description": "[parameters('description')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the scope map." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the scope map was created in." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the scope map." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/scopeMaps', parameters('registryName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_replications": { + "copy": { + "name": "registry_replications", + "count": "[length(coalesce(parameters('replications'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Replication-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('replications'), createArray())[copyIndex()].name]" + }, + "registryName": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[coalesce(parameters('replications'), createArray())[copyIndex()].location]" + }, + "regionEndpointEnabled": { + "value": "[tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'regionEndpointEnabled')]" + }, + "zoneRedundancy": { + "value": "[tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'zoneRedundancy')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('replications'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "8531695368487734118" + }, + "name": "Azure Container Registry (ACR) Replications", + "description": "This module deploys an Azure Container Registry (ACR) Replication.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the replication." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "regionEndpointEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specifies whether the replication regional endpoint is enabled. Requests will not be routed to a replication whose regional endpoint is disabled, however its data will continue to be synced with other replications." + } + }, + "zoneRedundancy": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Whether or not zone redundancy is enabled for this container registry." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "replication": { + "type": "Microsoft.ContainerRegistry/registries/replications", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "regionEndpointEnabled": "[parameters('regionEndpointEnabled')]", + "zoneRedundancy": "[parameters('zoneRedundancy')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the replication." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the replication." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/replications', parameters('registryName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the replication was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('replication', '2023-06-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_credentialSets": { + "copy": { + "name": "registry_credentialSets", + "count": "[length(coalesce(parameters('credentialSets'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-CredentialSet-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].name]" + }, + "registryName": { + "value": "[parameters('name')]" + }, + "managedIdentities": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].managedIdentities]" + }, + "authCredentials": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].authCredentials]" + }, + "loginServer": { + "value": "[coalesce(parameters('credentialSets'), createArray())[copyIndex()].loginServer]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "12196074162662855376" + }, + "name": "Container Registries Credential Sets", + "description": "This module deploys an ACR Credential Set.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + } + } + }, + "authCredentialsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the credential." + } + }, + "usernameSecretIdentifier": { + "type": "string", + "metadata": { + "description": "Required. KeyVault Secret URI for accessing the username." + } + }, + "passwordSecretIdentifier": { + "type": "string", + "metadata": { + "description": "Required. KeyVault Secret URI for accessing the password." + } + } + } + } + } + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the credential set." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Required. The managed identity definition for this resource." + } + }, + "authCredentials": { + "$ref": "#/definitions/authCredentialsType", + "metadata": { + "description": "Required. List of authentication credentials stored for an upstream. Usually consists of a primary and an optional secondary credential." + } + }, + "loginServer": { + "type": "string", + "metadata": { + "description": "Required. The credentials are stored for this upstream or login server." + } + } + }, + "variables": { + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), 'SystemAssigned', null())), null())]" + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "credentialSet": { + "type": "Microsoft.ContainerRegistry/registries/credentialSets", + "apiVersion": "2023-11-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "identity": "[variables('identity')]", + "properties": { + "authCredentials": "[parameters('authCredentials')]", + "loginServer": "[parameters('loginServer')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Credential Set." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Credential Set." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Credential Set." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/credentialSets', parameters('registryName'), parameters('name'))]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('credentialSet', '2023-11-01-preview', 'full'), 'identity'), 'principalId'), '')]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_cacheRules": { + "copy": { + "name": "registry_cacheRules", + "count": "[length(coalesce(parameters('cacheRules'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Cache-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "registryName": { + "value": "[parameters('name')]" + }, + "sourceRepository": { + "value": "[coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'name'), replace(replace(coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository, '/', '-'), '.', '-'))]" + }, + "targetRepository": { + "value": "[coalesce(tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'targetRepository'), coalesce(parameters('cacheRules'), createArray())[copyIndex()].sourceRepository)]" + }, + "credentialSetResourceId": { + "value": "[tryGet(coalesce(parameters('cacheRules'), createArray())[copyIndex()], 'credentialSetResourceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "4294329625336671928" + }, + "name": "Container Registries Cache", + "description": "Cache for Azure Container Registry (Preview) feature allows users to cache container images in a private container registry. Cache for ACR, is a preview feature available in Basic, Standard, and Premium service tiers ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache)).", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[replace(replace(parameters('sourceRepository'), '/', '-'), '.', '-')]", + "metadata": { + "description": "Optional. The name of the cache rule. Will be dereived from the source repository name if not defined." + } + }, + "sourceRepository": { + "type": "string", + "metadata": { + "description": "Required. Source repository pulled from upstream." + } + }, + "targetRepository": { + "type": "string", + "defaultValue": "[parameters('sourceRepository')]", + "metadata": { + "description": "Optional. Target repository specified in docker pull command. E.g.: docker pull myregistry.azurecr.io/{targetRepository}:{tag}." + } + }, + "credentialSetResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the credential store which is associated with the cache rule." + } + } + }, + "resources": [ + { + "type": "Microsoft.ContainerRegistry/registries/cacheRules", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "properties": { + "sourceRepository": "[parameters('sourceRepository')]", + "targetRepository": "[parameters('targetRepository')]", + "credentialSetResourceId": "[parameters('credentialSetResourceId')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Cache Rule." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Cache Rule." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Cache Rule." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/cacheRules', parameters('registryName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "registry", + "registry_credentialSets" + ] + }, + "registry_webhooks": { + "copy": { + "name": "registry_webhooks", + "count": "[length(coalesce(parameters('webhooks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Registry-Webhook-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('webhooks'), createArray())[copyIndex()].name]" + }, + "registryName": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'location'), parameters('location'))]" + }, + "action": { + "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'action'), createArray('chart_delete', 'chart_push', 'delete', 'push', 'quarantine'))]" + }, + "customHeaders": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'customHeaders')]" + }, + "scope": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'scope')]" + }, + "status": { + "value": "[tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'status')]" + }, + "serviceUri": { + "value": "[coalesce(parameters('webhooks'), createArray())[copyIndex()].serviceUri]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('webhooks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "14912363209364245195" + }, + "name": "Azure Container Registry (ACR) Webhooks", + "description": "This module deploys an Azure Container Registry (ACR) Webhook.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "registryName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent registry. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}webhook', parameters('registryName'))]", + "minLength": 5, + "maxLength": 50, + "metadata": { + "description": "Optional. The name of the registry webhook." + } + }, + "serviceUri": { + "type": "string", + "metadata": { + "description": "Required. The service URI for the webhook to post notifications." + } + }, + "status": { + "type": "string", + "defaultValue": "enabled", + "allowedValues": [ + "disabled", + "enabled" + ], + "metadata": { + "description": "Optional. The status of the webhook at the time the operation was called." + } + }, + "action": { + "type": "array", + "defaultValue": [ + "chart_delete", + "chart_push", + "delete", + "push", + "quarantine" + ], + "metadata": { + "description": "Optional. The list of actions that trigger the webhook to post notifications." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "customHeaders": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Custom headers that will be added to the webhook notifications." + } + }, + "scope": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The scope of repositories where the event can be triggered. For example, 'foo:*' means events for all tags under repository 'foo'. 'foo:bar' means events for 'foo:bar' only. 'foo' is equivalent to 'foo:latest'. Empty means all events." + } + } + }, + "resources": { + "registry": { + "existing": true, + "type": "Microsoft.ContainerRegistry/registries", + "apiVersion": "2023-06-01-preview", + "name": "[parameters('registryName')]" + }, + "webhook": { + "type": "Microsoft.ContainerRegistry/registries/webhooks", + "apiVersion": "2023-06-01-preview", + "name": "[format('{0}/{1}', parameters('registryName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "actions": "[parameters('action')]", + "customHeaders": "[parameters('customHeaders')]", + "scope": "[parameters('scope')]", + "serviceUri": "[parameters('serviceUri')]", + "status": "[parameters('status')]" + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the webhook." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries/webhooks', parameters('registryName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the webhook." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Azure container registry." + }, + "value": "[resourceGroup().name]" + }, + "actions": { + "type": "array", + "metadata": { + "description": "The actions of the webhook." + }, + "value": "[reference('webhook').actions]" + }, + "status": { + "type": "string", + "metadata": { + "description": "The status of the webhook." + }, + "value": "[reference('webhook').status]" + }, + "provistioningState": { + "type": "string", + "metadata": { + "description": "The provisioning state of the webhook." + }, + "value": "[reference('webhook').provisioningState]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('webhook', '2023-06-01-preview', 'full').location]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + }, + "registry_privateEndpoints": { + "copy": { + "name": "registry_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-registry-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'registry')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroupName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" + }, + "privateDnsZoneResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "4120048060064073955" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "ipConfigurationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "manualPrivateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "$ref": "#/definitions/ipConfigurationsType", + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + } + }, + "privateLinkServiceConnections": { + "$ref": "#/definitions/privateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneResourceIds')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" + }, + "privateDNSResourceIds": { + "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11244630631275470040" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + } + } + } + }, + "dependsOn": [ + "registry" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The Name of the Azure container registry." + }, + "value": "[parameters('name')]" + }, + "loginServer": { + "type": "string", + "metadata": { + "description": "The reference to the Azure container registry." + }, + "value": "[reference(resourceId('Microsoft.ContainerRegistry/registries', parameters('name')), '2019-05-01').loginServer]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the Azure container registry." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Azure container registry." + }, + "value": "[resourceId('Microsoft.ContainerRegistry/registries', parameters('name'))]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('registry', '2023-06-01-preview', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('registry', '2023-06-01-preview', 'full').location]" + }, + "credentialSetsSystemAssignedMIPrincipalIds": { + "type": "array", + "metadata": { + "description": "The Principal IDs of the ACR Credential Sets system-assigned identities." + }, + "copy": { + "count": "[length(range(0, length(parameters('credentialSets'))))]", + "input": "[reference(format('registry_credentialSets[{0}]', range(0, length(parameters('credentialSets')))[copyIndex()])).outputs.systemAssignedMIPrincipalId.value]" + } + }, + "credentialSetsResourceIds": { + "type": "array", + "metadata": { + "description": "The Resource IDs of the ACR Credential Sets." + }, + "copy": { + "count": "[length(range(0, length(parameters('credentialSets'))))]", + "input": "[reference(format('registry_credentialSets[{0}]', range(0, length(parameters('credentialSets')))[copyIndex()])).outputs.resourceId.value]" + } + } + } + } + }, + "dependsOn": [ + "acrPrivateDNSZone", + "newVnet", + "userAssignedIdentity" + ] + }, + "newVnet": { + "condition": "[equals(parameters('networkingConfiguration').networkType, 'createNew')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('vnet-{0}', uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('vnet-{0}-{1}', parameters('namingPrefix'), uniqueString(resourceGroup().id))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "addressPrefixes": { + "value": [ + "[parameters('networkingConfiguration').addressSpace]" + ] + }, + "subnets": { + "value": "[union(if(parameters('privateNetworking'), createArray(createObject('name', coalesce(tryGet(parameters('networkingConfiguration'), 'containerRegistryPrivateEndpointSubnetName'), 'acr-subnet'), 'addressPrefix', coalesce(tryGet(parameters('networkingConfiguration'), 'containerRegistrySubnetPrefix'), '10.0.0.32/29'))), createArray()), if(or(and(parameters('privateNetworking'), equals(parameters('selfHostedConfig').selfHostedType, 'azuredevops')), contains(parameters('computeTypes'), 'azure-container-instance')), createArray(createObject('name', coalesce(tryGet(parameters('networkingConfiguration'), 'containerInstanceSubnetName'), 'aci-subnet'), 'addressPrefix', coalesce(tryGet(parameters('networkingConfiguration'), 'containerInstanceSubnetAddressPrefix'), '10.0.2.0/24'), 'natGatewayResourceId', if(and(empty(coalesce(tryGet(parameters('networkingConfiguration'), 'natGatewayResourceId'), '')), parameters('privateNetworking')), reference('natGateway').outputs.resourceId.value, coalesce(tryGet(parameters('networkingConfiguration'), 'natGatewayResourceId'), '')), 'delegations', createArray(createObject('name', 'Microsoft.ContainerInstance/containerGroups', 'properties', createObject('serviceName', 'Microsoft.ContainerInstance/containerGroups'))))), createArray()), if(contains(parameters('computeTypes'), 'azure-container-app'), createArray(createObject('name', coalesce(tryGet(parameters('networkingConfiguration'), 'containerAppSubnetName'), 'app-subnet'), 'addressPrefix', coalesce(tryGet(parameters('networkingConfiguration'), 'containerAppSubnetAddressPrefix'), '10.0.1.0/24'), 'natGatewayResourceId', if(and(empty(coalesce(tryGet(parameters('networkingConfiguration'), 'natGatewayResourceId'), '')), parameters('privateNetworking')), reference('natGateway').outputs.resourceId.value, coalesce(tryGet(parameters('networkingConfiguration'), 'natGatewayResourceId'), '')), 'delegations', createArray(createObject('name', 'Microsoft.App.environments', 'properties', createObject('serviceName', 'Microsoft.App/environments'))))), createArray()), if(and(and(contains(parameters('computeTypes'), 'azure-container-app'), parameters('privateNetworking')), equals(parameters('selfHostedConfig').selfHostedType, 'azuredevops')), createArray(createObject('name', coalesce(tryGet(parameters('networkingConfiguration'), 'containerAppDeploymentScriptSubnetName'), 'app-deployment-script-subnet'), 'addressPrefix', coalesce(tryGet(parameters('networkingConfiguration'), 'containerAppDeploymentScriptSubnetPrefix'), '10.0.3.0/29'))), createArray()), createArray())]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "2754811334012865077" + }, + "name": "Virtual Networks", + "description": "This module deploys a Virtual Network (vNet).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Virtual Network (vNet)." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "addressPrefixes": { + "type": "array", + "metadata": { + "description": "Required. An Array of 1 or more IP Address Prefixes for the Virtual Network." + } + }, + "subnets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An Array of subnets to deploy to the Virtual Network." + } + }, + "dnsServers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. DNS Servers associated to the Virtual Network." + } + }, + "ddosProtectionPlanResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "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." + } + }, + "peerings": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Virtual Network Peerings configurations." + } + }, + "vnetEncryption": { + "type": "bool", + "defaultValue": false, + "metadata": { + "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." + } + }, + "vnetEncryptionEnforcement": { + "type": "string", + "defaultValue": "AllowUnencrypted", + "allowedValues": [ + "AllowUnencrypted", + "DropUnencrypted" + ], + "metadata": { + "description": "Optional. If the encrypted VNet allows VM that does not support encryption. Can only be used when vnetEncryption is enabled." + } + }, + "flowTimeoutInMinutes": { + "type": "int", + "defaultValue": 0, + "maxValue": 30, + "metadata": { + "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." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-virtualnetwork.{0}.{1}', replace('0.2.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "virtualNetwork": { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "subnets", + "count": "[length(parameters('subnets'))]", + "input": { + "name": "[parameters('subnets')[copyIndex('subnets')].name]", + "properties": { + "addressPrefix": "[parameters('subnets')[copyIndex('subnets')].addressPrefix]", + "addressPrefixes": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'addressPrefixes'), parameters('subnets')[copyIndex('subnets')].addressPrefixes, createArray())]", + "applicationGatewayIPConfigurations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'applicationGatewayIPConfigurations'), parameters('subnets')[copyIndex('subnets')].applicationGatewayIPConfigurations, createArray())]", + "delegations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'delegations'), parameters('subnets')[copyIndex('subnets')].delegations, createArray())]", + "ipAllocations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'ipAllocations'), parameters('subnets')[copyIndex('subnets')].ipAllocations, createArray())]", + "natGateway": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'natGatewayResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].natGatewayResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].natGatewayResourceId), null())]", + "networkSecurityGroup": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'networkSecurityGroupResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].networkSecurityGroupResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].networkSecurityGroupResourceId), null())]", + "privateEndpointNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'privateEndpointNetworkPolicies'), parameters('subnets')[copyIndex('subnets')].privateEndpointNetworkPolicies, null())]", + "privateLinkServiceNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'privateLinkServiceNetworkPolicies'), parameters('subnets')[copyIndex('subnets')].privateLinkServiceNetworkPolicies, null())]", + "routeTable": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'routeTableResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].routeTableResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].routeTableResourceId), null())]", + "serviceEndpoints": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'serviceEndpoints'), parameters('subnets')[copyIndex('subnets')].serviceEndpoints, createArray())]", + "serviceEndpointPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'serviceEndpointPolicies'), parameters('subnets')[copyIndex('subnets')].serviceEndpointPolicies, createArray())]" + } + } + } + ], + "addressSpace": { + "addressPrefixes": "[parameters('addressPrefixes')]" + }, + "ddosProtectionPlan": "[if(not(empty(parameters('ddosProtectionPlanResourceId'))), createObject('id', parameters('ddosProtectionPlanResourceId')), null())]", + "dhcpOptions": "[if(not(empty(parameters('dnsServers'))), createObject('dnsServers', array(parameters('dnsServers'))), null())]", + "enableDdosProtection": "[not(empty(parameters('ddosProtectionPlanResourceId')))]", + "encryption": "[if(equals(parameters('vnetEncryption'), true()), createObject('enabled', parameters('vnetEncryption'), 'enforcement', parameters('vnetEncryptionEnforcement')), null())]", + "flowTimeoutInMinutes": "[if(not(equals(parameters('flowTimeoutInMinutes'), 0)), parameters('flowTimeoutInMinutes'), null())]" + } + }, + "virtualNetwork_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_diagnosticSettings": { + "copy": { + "name": "virtualNetwork_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_roleAssignments": { + "copy": { + "name": "virtualNetwork_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_subnets": { + "copy": { + "name": "virtualNetwork_subnets", + "count": "[length(parameters('subnets'))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-subnet-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualNetworkName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('subnets')[copyIndex()].name]" + }, + "addressPrefix": { + "value": "[parameters('subnets')[copyIndex()].addressPrefix]" + }, + "addressPrefixes": "[if(contains(parameters('subnets')[copyIndex()], 'addressPrefixes'), createObject('value', parameters('subnets')[copyIndex()].addressPrefixes), createObject('value', createArray()))]", + "applicationGatewayIPConfigurations": "[if(contains(parameters('subnets')[copyIndex()], 'applicationGatewayIPConfigurations'), createObject('value', parameters('subnets')[copyIndex()].applicationGatewayIPConfigurations), createObject('value', createArray()))]", + "delegations": "[if(contains(parameters('subnets')[copyIndex()], 'delegations'), createObject('value', parameters('subnets')[copyIndex()].delegations), createObject('value', createArray()))]", + "ipAllocations": "[if(contains(parameters('subnets')[copyIndex()], 'ipAllocations'), createObject('value', parameters('subnets')[copyIndex()].ipAllocations), createObject('value', createArray()))]", + "natGatewayResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'natGatewayResourceId'), createObject('value', parameters('subnets')[copyIndex()].natGatewayResourceId), createObject('value', ''))]", + "networkSecurityGroupResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'networkSecurityGroupResourceId'), createObject('value', parameters('subnets')[copyIndex()].networkSecurityGroupResourceId), createObject('value', ''))]", + "privateEndpointNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'privateEndpointNetworkPolicies'), createObject('value', parameters('subnets')[copyIndex()].privateEndpointNetworkPolicies), createObject('value', ''))]", + "privateLinkServiceNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'privateLinkServiceNetworkPolicies'), createObject('value', parameters('subnets')[copyIndex()].privateLinkServiceNetworkPolicies), createObject('value', ''))]", + "roleAssignments": "[if(contains(parameters('subnets')[copyIndex()], 'roleAssignments'), createObject('value', parameters('subnets')[copyIndex()].roleAssignments), createObject('value', createArray()))]", + "routeTableResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'routeTableResourceId'), createObject('value', parameters('subnets')[copyIndex()].routeTableResourceId), createObject('value', ''))]", + "serviceEndpointPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'serviceEndpointPolicies'), createObject('value', parameters('subnets')[copyIndex()].serviceEndpointPolicies), createObject('value', createArray()))]", + "serviceEndpoints": "[if(contains(parameters('subnets')[copyIndex()], 'serviceEndpoints'), createObject('value', parameters('subnets')[copyIndex()].serviceEndpoints), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "17087074157977382192" + }, + "name": "Virtual Network Subnets", + "description": "This module deploys a Virtual Network Subnet.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Optional. The Name of the subnet resource." + } + }, + "virtualNetworkName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual network. Required if the template is used in a standalone deployment." + } + }, + "addressPrefix": { + "type": "string", + "metadata": { + "description": "Required. The address prefix for the subnet." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the network security group to assign to the subnet." + } + }, + "routeTableResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpoints": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "delegations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The delegations to enable on the subnet." + } + }, + "natGatewayResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "Disabled", + "Enabled", + "" + ], + "metadata": { + "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "Disabled", + "Enabled", + "" + ], + "metadata": { + "description": "Optional. enable or disable apply network policies on private link service in the subnet." + } + }, + "addressPrefixes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of address prefixes for the subnet." + } + }, + "applicationGatewayIPConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application gateway IP configurations of virtual network resource." + } + }, + "ipAllocations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of IpAllocation which reference this subnet." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "virtualNetwork": { + "existing": true, + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2023-11-01", + "name": "[parameters('virtualNetworkName')]" + }, + "subnet": { + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "properties": { + "addressPrefix": "[parameters('addressPrefix')]", + "networkSecurityGroup": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('id', parameters('networkSecurityGroupResourceId')), null())]", + "routeTable": "[if(not(empty(parameters('routeTableResourceId'))), createObject('id', parameters('routeTableResourceId')), null())]", + "natGateway": "[if(not(empty(parameters('natGatewayResourceId'))), createObject('id', parameters('natGatewayResourceId')), null())]", + "serviceEndpoints": "[parameters('serviceEndpoints')]", + "delegations": "[parameters('delegations')]", + "privateEndpointNetworkPolicies": "[if(not(empty(parameters('privateEndpointNetworkPolicies'))), parameters('privateEndpointNetworkPolicies'), null())]", + "privateLinkServiceNetworkPolicies": "[if(not(empty(parameters('privateLinkServiceNetworkPolicies'))), parameters('privateLinkServiceNetworkPolicies'), null())]", + "addressPrefixes": "[parameters('addressPrefixes')]", + "applicationGatewayIPConfigurations": "[parameters('applicationGatewayIPConfigurations')]", + "ipAllocations": "[parameters('ipAllocations')]", + "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "subnet_roleAssignments": { + "copy": { + "name": "subnet_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}/subnets/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "subnet" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name'))]" + }, + "subnetAddressPrefix": { + "type": "string", + "metadata": { + "description": "The address prefix for the subnet." + }, + "value": "[reference('subnet').addressPrefix]" + }, + "subnetAddressPrefixes": { + "type": "array", + "metadata": { + "description": "List of address prefixes for the subnet." + }, + "value": "[if(not(empty(parameters('addressPrefixes'))), reference('subnet').addressPrefixes, createArray())]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_peering_local": { + "copy": { + "name": "virtualNetwork_peering_local", + "count": "[length(parameters('peerings'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-virtualNetworkPeering-local-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "localVnetName": { + "value": "[parameters('name')]" + }, + "remoteVirtualNetworkId": { + "value": "[parameters('peerings')[copyIndex()].remoteVirtualNetworkId]" + }, + "name": "[if(contains(parameters('peerings')[copyIndex()], 'name'), createObject('value', parameters('peerings')[copyIndex()].name), createObject('value', format('{0}-{1}', parameters('name'), last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')))))]", + "allowForwardedTraffic": "[if(contains(parameters('peerings')[copyIndex()], 'allowForwardedTraffic'), createObject('value', parameters('peerings')[copyIndex()].allowForwardedTraffic), createObject('value', true()))]", + "allowGatewayTransit": "[if(contains(parameters('peerings')[copyIndex()], 'allowGatewayTransit'), createObject('value', parameters('peerings')[copyIndex()].allowGatewayTransit), createObject('value', false()))]", + "allowVirtualNetworkAccess": "[if(contains(parameters('peerings')[copyIndex()], 'allowVirtualNetworkAccess'), createObject('value', parameters('peerings')[copyIndex()].allowVirtualNetworkAccess), createObject('value', true()))]", + "doNotVerifyRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'doNotVerifyRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].doNotVerifyRemoteGateways), createObject('value', true()))]", + "useRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'useRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].useRemoteGateways), createObject('value', false()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "1725716682351191048" + }, + "name": "Virtual Network Peerings", + "description": "This module deploys a Virtual Network Peering.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[format('{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkId'), '/')))]", + "metadata": { + "description": "Optional. The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + } + }, + "localVnetName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." + } + }, + "remoteVirtualNetworkId": { + "type": "string", + "metadata": { + "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." + } + }, + "allowForwardedTraffic": { + "type": "bool", + "defaultValue": true, + "metadata": { + "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." + } + }, + "allowGatewayTransit": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "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." + } + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. If we need to verify the provisioning state of the remote gateway. Default is true." + } + }, + "useRemoteGateways": { + "type": "bool", + "defaultValue": false, + "metadata": { + "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." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", + "properties": { + "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", + "allowGatewayTransit": "[parameters('allowGatewayTransit')]", + "allowVirtualNetworkAccess": "[parameters('allowVirtualNetworkAccess')]", + "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", + "useRemoteGateways": "[parameters('useRemoteGateways')]", + "remoteVirtualNetwork": { + "id": "[parameters('remoteVirtualNetworkId')]" + } + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/virtualNetworkPeerings', parameters('localVnetName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_peering_remote": { + "copy": { + "name": "virtualNetwork_peering_remote", + "count": "[length(parameters('peerings'))]" + }, + "condition": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringEnabled'), equals(parameters('peerings')[copyIndex()].remotePeeringEnabled, true()), false())]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-virtualNetworkPeering-remote-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')[2]]", + "resourceGroup": "[split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "localVnetName": { + "value": "[last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/'))]" + }, + "remoteVirtualNetworkId": { + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" + }, + "name": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringName'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringName), createObject('value', format('{0}-{1}', last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')), parameters('name'))))]", + "allowForwardedTraffic": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowForwardedTraffic'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowForwardedTraffic), createObject('value', true()))]", + "allowGatewayTransit": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowGatewayTransit'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowGatewayTransit), createObject('value', false()))]", + "allowVirtualNetworkAccess": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowVirtualNetworkAccess'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowVirtualNetworkAccess), createObject('value', true()))]", + "doNotVerifyRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringDoNotVerifyRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringDoNotVerifyRemoteGateways), createObject('value', true()))]", + "useRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringUseRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringUseRemoteGateways), createObject('value', false()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "1725716682351191048" + }, + "name": "Virtual Network Peerings", + "description": "This module deploys a Virtual Network Peering.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[format('{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkId'), '/')))]", + "metadata": { + "description": "Optional. The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + } + }, + "localVnetName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." + } + }, + "remoteVirtualNetworkId": { + "type": "string", + "metadata": { + "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." + } + }, + "allowForwardedTraffic": { + "type": "bool", + "defaultValue": true, + "metadata": { + "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." + } + }, + "allowGatewayTransit": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "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." + } + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. If we need to verify the provisioning state of the remote gateway. Default is true." + } + }, + "useRemoteGateways": { + "type": "bool", + "defaultValue": false, + "metadata": { + "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." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", + "properties": { + "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", + "allowGatewayTransit": "[parameters('allowGatewayTransit')]", + "allowVirtualNetworkAccess": "[parameters('allowVirtualNetworkAccess')]", + "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", + "useRemoteGateways": "[parameters('useRemoteGateways')]", + "remoteVirtualNetwork": { + "id": "[parameters('remoteVirtualNetworkId')]" + } + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/virtualNetworkPeerings', parameters('localVnetName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network." + }, + "value": "[parameters('name')]" + }, + "subnetNames": { + "type": "array", + "metadata": { + "description": "The names of the deployed subnets." + }, + "copy": { + "count": "[length(parameters('subnets'))]", + "input": "[parameters('subnets')[copyIndex()].name]" + } + }, + "subnetResourceIds": { + "type": "array", + "metadata": { + "description": "The resource IDs of the deployed subnets." + }, + "copy": { + "count": "[length(parameters('subnets'))]", + "input": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('name'), parameters('subnets')[copyIndex()].name)]" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetwork', '2023-11-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "natGateway" + ] + }, + "appEnvironment": { + "condition": "[contains(parameters('computeTypes'), 'azure-container-app')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('appEnv-{0}', uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('appEnv{0}{1}', parameters('namingPrefix'), uniqueString(resourceGroup().id))]" + }, + "logAnalyticsWorkspaceResourceId": { + "value": "[reference('logAnalyticsWokrspace').outputs.resourceId.value]" + }, + "location": { + "value": "[parameters('location')]" + }, + "infrastructureSubnetId": "[if(equals(parameters('networkingConfiguration').networkType, 'createNew'), createObject('value', filter(reference('newVnet').outputs.subnetResourceIds.value, lambda('subnetId', contains(lambdaVariables('subnetId'), coalesce(tryGet(parameters('networkingConfiguration'), 'containerAppSubnetName'), 'app-subnet'))))[0]), if(equals(parameters('networkingConfiguration').networkType, 'useExisting'), createObject('value', format('{0}/subnets/{1}', parameters('networkingConfiguration').virtualNetworkResourceId, parameters('networkingConfiguration').computeNetworking.containerAppSubnetName)), createObject('value', null())))]", + "zoneRedundant": { + "value": true + }, + "internal": "[if(parameters('privateNetworking'), createObject('value', true()), createObject('value', false()))]", + "workloadProfiles": { + "value": [ + { + "name": "consumption", + "workloadProfileType": "consumption" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "11084948673769328524" + }, + "name": "App ManagedEnvironments", + "description": "This module deploys an App Managed Environment (also known as a Container App Environment).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "storageType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "accessMode": { + "type": "string", + "allowedValues": [ + "ReadOnly", + "ReadWrite" + ], + "metadata": { + "description": "Required. Access mode for storage: \"ReadOnly\" or \"ReadWrite\"." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "NFS", + "SMB" + ], + "metadata": { + "description": "Required. Type of storage: \"SMB\" or \"NFS\"." + } + }, + "storageAccountName": { + "type": "string", + "metadata": { + "description": "Required. Storage account name." + } + }, + "shareName": { + "type": "string", + "metadata": { + "description": "Required. File share name." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Container Apps Managed Environment." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "Required. Existing Log Analytics Workspace resource ID. Note: This value is not required as per the resource type. However, not providing it currently causes an issue that is tracked [here](https://github.com/Azure/bicep/issues/9990)." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "logsDestination": { + "type": "string", + "defaultValue": "log-analytics", + "metadata": { + "description": "Optional. Logs destination." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "appInsightsConnectionString": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Optional. Application Insights connection string." + } + }, + "daprAIConnectionString": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Optional. Application Insights connection string used by Dapr to export Service to Service communication telemetry." + } + }, + "daprAIInstrumentationKey": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Optional. Azure Monitor instrumentation key used by Dapr to export Service to Service communication telemetry." + } + }, + "dockerBridgeCidr": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. CIDR notation IP range assigned to the Docker bridge, network. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant." + } + }, + "infrastructureSubnetId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. Resource ID of a subnet for infrastructure components. This is used to deploy the environment into a virtual network. Must not overlap with any other provided IP ranges. Required if \"internal\" is set to true. Required if zoneRedundant is set to true to make the resource WAF compliant." + } + }, + "internal": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Conditional. Boolean indicating the environment only has an internal load balancer. These environments do not have a public static IP resource. If set to true, then \"infrastructureSubnetId\" must be provided. Required if zoneRedundant is set to true to make the resource WAF compliant." + } + }, + "platformReservedCidr": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. IP range in CIDR notation that can be reserved for environment infrastructure IP addresses. It must not overlap with any other provided IP ranges and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant." + } + }, + "platformReservedDnsIP": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. An IP address from the IP range defined by \"platformReservedCidr\" that will be reserved for the internal DNS server. It must not be the first address in the range and can only be used when the environment is deployed into a virtual network. If not provided, it will be set with a default value by the platform. Required if zoneRedundant is set to true to make the resource WAF compliant." + } + }, + "zoneRedundant": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Whether or not this Managed Environment is zone-redundant." + } + }, + "certificatePassword": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Optional. Password of the certificate used by the custom domain." + } + }, + "certificateValue": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Optional. Certificate to use for the custom domain. PFX or PEM." + } + }, + "dnsSuffix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. DNS suffix for the environment domain." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "openTelemetryConfiguration": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Open Telemetry configuration." + } + }, + "workloadProfiles": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Conditional. Workload profiles configured for the Managed Environment. Required if zoneRedundant is set to true to make the resource WAF compliant." + } + }, + "infrastructureResourceGroupName": { + "type": "string", + "defaultValue": "[take(format('ME_{0}', parameters('name')), 63)]", + "metadata": { + "description": "Conditional. Name of the infrastructure resource group. If not provided, it will be set with a default value. Required if zoneRedundant is set to true to make the resource WAF compliant." + } + }, + "storages": { + "$ref": "#/definitions/storageType", + "metadata": { + "description": "Optional. The list of storages to mount on the environment." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "managedEnvironment::storage": { + "copy": { + "name": "storage", + "count": "[length(coalesce(parameters('storages'), createArray()))]" + }, + "type": "Microsoft.App/managedEnvironments/storages", + "apiVersion": "2023-11-02-preview", + "name": "[format('{0}/{1}', parameters('name'), coalesce(parameters('storages'), createArray())[copyIndex()].shareName)]", + "properties": { + "nfsAzureFile": "[if(equals(coalesce(parameters('storages'), createArray())[copyIndex()].kind, 'NFS'), createObject('accessMode', coalesce(parameters('storages'), createArray())[copyIndex()].accessMode, 'server', format('{0}.file.{1}', coalesce(parameters('storages'), createArray())[copyIndex()].storageAccountName, environment().suffixes.storage), 'shareName', format('/{0}/{1}', coalesce(parameters('storages'), createArray())[copyIndex()].storageAccountName, coalesce(parameters('storages'), createArray())[copyIndex()].shareName)), null())]", + "azureFile": "[if(equals(coalesce(parameters('storages'), createArray())[copyIndex()].kind, 'SMB'), createObject('accessMode', coalesce(parameters('storages'), createArray())[copyIndex()].accessMode, 'accountName', coalesce(parameters('storages'), createArray())[copyIndex()].storageAccountName, 'accountKey', listkeys(resourceId('Microsoft.Storage/storageAccounts', coalesce(parameters('storages'), createArray())[copyIndex()].storageAccountName), '2023-01-01').keys[0].value, 'shareName', coalesce(parameters('storages'), createArray())[copyIndex()].shareName), null())]" + }, + "dependsOn": [ + "managedEnvironment" + ] + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.app-managedenvironment.{0}.{1}', replace('0.6.2', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "logAnalyticsWorkspace": { + "condition": "[not(empty(parameters('logAnalyticsWorkspaceResourceId')))]", + "existing": true, + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2023-09-01", + "subscriptionId": "[split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]]", + "name": "[last(split(parameters('logAnalyticsWorkspaceResourceId'), '/'))]" + }, + "managedEnvironment": { + "type": "Microsoft.App/managedEnvironments", + "apiVersion": "2023-11-02-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "properties": { + "appInsightsConfiguration": { + "connectionString": "[parameters('appInsightsConnectionString')]" + }, + "appLogsConfiguration": { + "destination": "[parameters('logsDestination')]", + "logAnalyticsConfiguration": { + "customerId": "[reference('logAnalyticsWorkspace').customerId]", + "sharedKey": "[listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', last(split(parameters('logAnalyticsWorkspaceResourceId'), '/'))), '2023-09-01').primarySharedKey]" + } + }, + "daprAIConnectionString": "[parameters('daprAIConnectionString')]", + "daprAIInstrumentationKey": "[parameters('daprAIInstrumentationKey')]", + "customDomainConfiguration": { + "certificatePassword": "[parameters('certificatePassword')]", + "certificateValue": "[if(not(empty(parameters('certificateValue'))), parameters('certificateValue'), null())]", + "dnsSuffix": "[parameters('dnsSuffix')]" + }, + "openTelemetryConfiguration": "[if(not(empty(parameters('openTelemetryConfiguration'))), parameters('openTelemetryConfiguration'), null())]", + "vnetConfiguration": { + "internal": "[parameters('internal')]", + "infrastructureSubnetId": "[if(not(empty(parameters('infrastructureSubnetId'))), parameters('infrastructureSubnetId'), null())]", + "dockerBridgeCidr": "[if(not(empty(parameters('infrastructureSubnetId'))), parameters('dockerBridgeCidr'), null())]", + "platformReservedCidr": "[if(and(empty(parameters('workloadProfiles')), not(empty(parameters('infrastructureSubnetId')))), parameters('platformReservedCidr'), null())]", + "platformReservedDnsIP": "[if(and(empty(parameters('workloadProfiles')), not(empty(parameters('infrastructureSubnetId')))), parameters('platformReservedDnsIP'), null())]" + }, + "workloadProfiles": "[if(not(empty(parameters('workloadProfiles'))), parameters('workloadProfiles'), null())]", + "zoneRedundant": "[parameters('zoneRedundant')]", + "infrastructureResourceGroup": "[parameters('infrastructureResourceGroupName')]" + }, + "dependsOn": [ + "logAnalyticsWorkspace" + ] + }, + "managedEnvironment_roleAssignments": { + "copy": { + "name": "managedEnvironment_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.App/managedEnvironments/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.App/managedEnvironments', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "managedEnvironment" + ] + }, + "managedEnvironment_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.App/managedEnvironments/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "managedEnvironment" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the Managed Environment was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('managedEnvironment', '2023-11-02-preview', 'full').location]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the Managed Environment." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Managed Environment." + }, + "value": "[resourceId('Microsoft.App/managedEnvironments', parameters('name'))]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('managedEnvironment', '2023-11-02-preview', 'full'), 'identity'), 'principalId'), '')]" + }, + "defaultDomain": { + "type": "string", + "metadata": { + "description": "The Default domain of the Managed Environment." + }, + "value": "[reference('managedEnvironment').defaultDomain]" + }, + "staticIp": { + "type": "string", + "metadata": { + "description": "The IP address of the Managed Environment." + }, + "value": "[reference('managedEnvironment').staticIp]" + } + } + } + }, + "dependsOn": [ + "logAnalyticsWokrspace", + "newVnet" + ] + }, + "natGatewayPublicIp": { + "condition": "[and(and(and(empty(coalesce(tryGet(parameters('networkingConfiguration'), 'natGatewayResourceId'), '')), empty(coalesce(tryGet(parameters('networkingConfiguration'), 'natGatewayPublicIpAddressResourceId'), ''))), equals(parameters('networkingConfiguration').networkType, 'createNew')), parameters('privateNetworking'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('natGatewayPublicIp-{0}', uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('natGatewayPublicIp-{0}', uniqueString(resourceGroup().id))]" + }, + "skuName": { + "value": "Standard" + }, + "publicIPAddressVersion": { + "value": "IPv4" + }, + "publicIPAllocationMethod": { + "value": "Static" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "14450344965065009842" + }, + "name": "Public IP Addresses", + "description": "This module deploys a Public IP Address.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "dnsSettingsType": { + "type": "object", + "properties": { + "domainNameLabel": { + "type": "string", + "metadata": { + "description": "Required. 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." + } + }, + "domainNameLabelScope": { + "type": "string", + "allowedValues": [ + "", + "NoReuse", + "ResourceGroupReuse", + "SubscriptionReuse", + "TenantReuse" + ], + "metadata": { + "description": "Required. The domain name label scope. If a domain name label and a domain name label scope are specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system with a hashed value includes in FQDN." + } + }, + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "reverseFqdn": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + } + } + }, + "ddosSettingsType": { + "type": "object", + "properties": { + "ddosProtectionPlan": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the DDOS protection plan associated with the public IP address." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan associated with the public IP address." + } + }, + "protectionMode": { + "type": "string", + "allowedValues": [ + "Enabled" + ], + "metadata": { + "description": "Required. The DDoS protection policy customizations." + } + } + } + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Public IP Address." + } + }, + "publicIpPrefixResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "publicIPAllocationMethod": { + "type": "string", + "defaultValue": "Static", + "allowedValues": [ + "Dynamic", + "Static" + ], + "metadata": { + "description": "Optional. The public IP address allocation method." + } + }, + "zones": { + "type": "array", + "items": { + "type": "int" + }, + "defaultValue": [ + 1, + 2, + 3 + ], + "allowedValues": [ + 1, + 2, + 3 + ], + "metadata": { + "description": "Optional. A list of availability zones denoting the IP allocated for the resource needs to come from." + } + }, + "publicIPAddressVersion": { + "type": "string", + "defaultValue": "IPv4", + "allowedValues": [ + "IPv4", + "IPv6" + ], + "metadata": { + "description": "Optional. IP address version." + } + }, + "dnsSettings": { + "$ref": "#/definitions/dnsSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DNS settings of the public IP address." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Basic", + "Standard" + ], + "metadata": { + "description": "Optional. Name of a public IP address SKU." + } + }, + "skuTier": { + "type": "string", + "defaultValue": "Regional", + "allowedValues": [ + "Global", + "Regional" + ], + "metadata": { + "description": "Optional. Tier of a public IP address SKU." + } + }, + "ddosSettings": { + "$ref": "#/definitions/ddosSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan configuration associated with the public IP address." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "idleTimeoutInMinutes": { + "type": "int", + "defaultValue": 4, + "metadata": { + "description": "Optional. The idle timeout of the public IP address." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.5.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "publicIpAddress": { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2023-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('skuName')]", + "tier": "[parameters('skuTier')]" + }, + "zones": "[map(parameters('zones'), lambda('zone', string(lambdaVariables('zone'))))]", + "properties": { + "ddosSettings": "[parameters('ddosSettings')]", + "dnsSettings": "[parameters('dnsSettings')]", + "publicIPAddressVersion": "[parameters('publicIPAddressVersion')]", + "publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]", + "publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]", + "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]", + "ipTags": null + } + }, + "publicIpAddress_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_roleAssignments": { + "copy": { + "name": "publicIpAddress_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_diagnosticSettings": { + "copy": { + "name": "publicIpAddress_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the public IP address was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the public IP address." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the public IP address." + }, + "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('name'))]" + }, + "ipAddress": { + "type": "string", + "metadata": { + "description": "The public IP address of the public IP address resource." + }, + "value": "[coalesce(tryGet(reference('publicIpAddress'), 'ipAddress'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('publicIpAddress', '2023-09-01', 'full').location]" + } + } + } + } + }, + "natGateway": { + "condition": "[and(and(parameters('privateNetworking'), empty(coalesce(tryGet(parameters('networkingConfiguration'), 'natGatewayResourceId'), ''))), equals(parameters('networkingConfiguration').networkType, 'createNew'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('natGateway-{0}', uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('natGateway-{0}-{1}', parameters('namingPrefix'), uniqueString(resourceGroup().id))]" + }, + "zone": { + "value": 0 + }, + "location": { + "value": "[parameters('location')]" + }, + "publicIpResourceIds": { + "value": [ + "[coalesce(tryGet(parameters('networkingConfiguration'), 'natGatewayPublicIpAddressResourceId'), reference('natGatewayPublicIp').outputs.resourceId.value)]" + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "16462724446247178543" + }, + "name": "NAT Gateways", + "description": "This module deploys a NAT Gateway.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Azure Bastion resource." + } + }, + "zone": { + "type": "int", + "allowedValues": [ + 0, + 1, + 2, + 3 + ], + "metadata": { + "description": "Required. A list of availability zones denoting the zone in which Nat Gateway should be deployed." + } + }, + "idleTimeoutInMinutes": { + "type": "int", + "defaultValue": 5, + "metadata": { + "description": "Optional. The idle timeout of the NAT gateway." + } + }, + "publicIpResourceIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Existing Public IP Address resource IDs to use for the NAT Gateway." + } + }, + "publicIPPrefixResourceIds": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Existing Public IP Prefixes resource IDs to use for the NAT Gateway." + } + }, + "publicIPAddressObjects": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the properties of the Public IPs to create and be used by the NAT Gateway." + } + }, + "publicIPPrefixObjects": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Specifies the properties of the Public IP Prefixes to create and be used by the NAT Gateway." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags for the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[take(format('46d3xbcp.res.network-natgateway.{0}.{1}', replace('1.1.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4)), 64)]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "natGateway": { + "type": "Microsoft.Network/natGateways", + "apiVersion": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "Standard" + }, + "properties": { + "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]", + "publicIpPrefixes": "[reference('formattedPublicIpPrefixResourceIds').outputs.formattedResourceIds.value]", + "publicIpAddresses": "[reference('formattedPublicIpResourceIds').outputs.formattedResourceIds.value]" + }, + "zones": "[if(not(equals(parameters('zone'), 0)), createArray(string(parameters('zone'))), null())]", + "dependsOn": [ + "formattedPublicIpPrefixResourceIds", + "formattedPublicIpResourceIds" + ] + }, + "natGateway_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/natGateways/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "natGateway" + ] + }, + "natGateway_roleAssignments": { + "copy": { + "name": "natGateway_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/natGateways/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/natGateways', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "natGateway" + ] + }, + "publicIPAddresses": { + "copy": { + "name": "publicIPAddresses", + "count": "[length(coalesce(parameters('publicIPAddressObjects'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-NatGw-PIP-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(contains(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'name'), createObject('value', coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()].name), createObject('value', format('{0}-pip', parameters('name'))))]", + "location": { + "value": "[parameters('location')]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "diagnosticSettings": { + "value": "[tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'diagnosticSettings')]" + }, + "publicIPAddressVersion": { + "value": "[tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'publicIPAddressVersion')]" + }, + "publicIPAllocationMethod": { + "value": "Static" + }, + "publicIpPrefixResourceId": { + "value": "[tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'publicIPPrefixResourceId')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "skuName": { + "value": "Standard" + }, + "skuTier": { + "value": "[tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'skuTier')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "zones": { + "value": "[coalesce(tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'zones'), if(not(equals(parameters('zone'), 0)), createArray(parameters('zone')), null()))]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "ddosSettings": { + "value": "[tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'ddosSettings')]" + }, + "dnsSettings": { + "value": "[tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'dnsSettings')]" + }, + "idleTimeoutInMinutes": { + "value": "[tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'idleTimeoutInMinutes')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "14450344965065009842" + }, + "name": "Public IP Addresses", + "description": "This module deploys a Public IP Address.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "dnsSettingsType": { + "type": "object", + "properties": { + "domainNameLabel": { + "type": "string", + "metadata": { + "description": "Required. 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." + } + }, + "domainNameLabelScope": { + "type": "string", + "allowedValues": [ + "", + "NoReuse", + "ResourceGroupReuse", + "SubscriptionReuse", + "TenantReuse" + ], + "metadata": { + "description": "Required. The domain name label scope. If a domain name label and a domain name label scope are specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system with a hashed value includes in FQDN." + } + }, + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "reverseFqdn": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + } + } + }, + "ddosSettingsType": { + "type": "object", + "properties": { + "ddosProtectionPlan": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the DDOS protection plan associated with the public IP address." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan associated with the public IP address." + } + }, + "protectionMode": { + "type": "string", + "allowedValues": [ + "Enabled" + ], + "metadata": { + "description": "Required. The DDoS protection policy customizations." + } + } + } + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Public IP Address." + } + }, + "publicIpPrefixResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "publicIPAllocationMethod": { + "type": "string", + "defaultValue": "Static", + "allowedValues": [ + "Dynamic", + "Static" + ], + "metadata": { + "description": "Optional. The public IP address allocation method." + } + }, + "zones": { + "type": "array", + "items": { + "type": "int" + }, + "defaultValue": [ + 1, + 2, + 3 + ], + "allowedValues": [ + 1, + 2, + 3 + ], + "metadata": { + "description": "Optional. A list of availability zones denoting the IP allocated for the resource needs to come from." + } + }, + "publicIPAddressVersion": { + "type": "string", + "defaultValue": "IPv4", + "allowedValues": [ + "IPv4", + "IPv6" + ], + "metadata": { + "description": "Optional. IP address version." + } + }, + "dnsSettings": { + "$ref": "#/definitions/dnsSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DNS settings of the public IP address." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Basic", + "Standard" + ], + "metadata": { + "description": "Optional. Name of a public IP address SKU." + } + }, + "skuTier": { + "type": "string", + "defaultValue": "Regional", + "allowedValues": [ + "Global", + "Regional" + ], + "metadata": { + "description": "Optional. Tier of a public IP address SKU." + } + }, + "ddosSettings": { + "$ref": "#/definitions/ddosSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan configuration associated with the public IP address." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "idleTimeoutInMinutes": { + "type": "int", + "defaultValue": 4, + "metadata": { + "description": "Optional. The idle timeout of the public IP address." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.5.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "publicIpAddress": { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2023-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('skuName')]", + "tier": "[parameters('skuTier')]" + }, + "zones": "[map(parameters('zones'), lambda('zone', string(lambdaVariables('zone'))))]", + "properties": { + "ddosSettings": "[parameters('ddosSettings')]", + "dnsSettings": "[parameters('dnsSettings')]", + "publicIPAddressVersion": "[parameters('publicIPAddressVersion')]", + "publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]", + "publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]", + "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]", + "ipTags": null + } + }, + "publicIpAddress_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_roleAssignments": { + "copy": { + "name": "publicIpAddress_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_diagnosticSettings": { + "copy": { + "name": "publicIpAddress_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the public IP address was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the public IP address." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the public IP address." + }, + "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('name'))]" + }, + "ipAddress": { + "type": "string", + "metadata": { + "description": "The public IP address of the public IP address resource." + }, + "value": "[coalesce(tryGet(reference('publicIpAddress'), 'ipAddress'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('publicIpAddress', '2023-09-01', 'full').location]" + } + } + } + } + }, + "formattedPublicIpResourceIds": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-formattedPublicIpResourceIds', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "generatedResourceIds": { + "copy": [ + { + "name": "value", + "count": "[length(coalesce(parameters('publicIPAddressObjects'), createArray()))]", + "input": "[reference(format('publicIPAddresses[{0}]', copyIndex('value'))).outputs.resourceId.value]" + } + ] + }, + "providedResourceIds": { + "value": "[parameters('publicIpResourceIds')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "15040145942768763519" + } + }, + "parameters": { + "generatedResourceIds": { + "type": "array", + "defaultValue": [] + }, + "providedResourceIds": { + "type": "array", + "defaultValue": [] + } + }, + "resources": [], + "outputs": { + "formattedResourceIds": { + "type": "array", + "copy": { + "count": "[length(concat(parameters('generatedResourceIds'), parameters('providedResourceIds')))]", + "input": { + "id": "[concat(parameters('generatedResourceIds'), parameters('providedResourceIds'))[copyIndex()]]" + } + } + } + } + } + }, + "dependsOn": [ + "publicIPAddresses" + ] + }, + "publicIPPrefixes": { + "copy": { + "name": "publicIPPrefixes", + "count": "[length(coalesce(parameters('publicIPPrefixObjects'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-NatGw-Prefix-PIP-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(contains(coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()], 'name'), createObject('value', coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()].name), createObject('value', format('{0}-pip', parameters('name'))))]", + "location": { + "value": "[parameters('location')]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "prefixLength": { + "value": "[coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()].prefixLength]" + }, + "customIPPrefix": { + "value": "[tryGet(coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()], 'customIPPrefix')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "2051021969117142516" + }, + "name": "Public IP Prefixes", + "description": "This module deploys a Public IP Prefix.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Required. The name of the Public IP Prefix." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "prefixLength": { + "type": "int", + "minValue": 28, + "maxValue": 31, + "metadata": { + "description": "Required. Length of the Public IP Prefix." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "customIPPrefix": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The custom IP address prefix 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." + } + }, + "zones": { + "type": "array", + "items": { + "type": "int" + }, + "defaultValue": [ + 1, + 2, + 3 + ], + "allowedValues": [ + 1, + 2, + 3 + ], + "metadata": { + "description": "Optional. A list of availability zones denoting the IP allocated for the resource needs to come from." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-publicipprefix.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "publicIpPrefix": { + "type": "Microsoft.Network/publicIPPrefixes", + "apiVersion": "2023-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "Standard" + }, + "zones": "[map(parameters('zones'), lambda('zone', string(lambdaVariables('zone'))))]", + "properties": { + "customIPPrefix": "[if(not(empty(parameters('customIPPrefix'))), parameters('customIPPrefix'), null())]", + "publicIPAddressVersion": "IPv4", + "prefixLength": "[parameters('prefixLength')]" + } + }, + "publicIpPrefix_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/publicIPPrefixes/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "publicIpPrefix" + ] + }, + "publicIpPrefix_roleAssignments": { + "copy": { + "name": "publicIpPrefix_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/publicIPPrefixes/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/publicIPPrefixes', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "publicIpPrefix" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the public IP prefix." + }, + "value": "[resourceId('Microsoft.Network/publicIPPrefixes', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the public IP prefix was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the public IP prefix." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('publicIpPrefix', '2023-09-01', 'full').location]" + } + } + } + } + }, + "formattedPublicIpPrefixResourceIds": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-formattedPublicIpPrefixResourceIds', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "generatedResourceIds": { + "copy": [ + { + "name": "value", + "count": "[length(coalesce(parameters('publicIPPrefixObjects'), createArray()))]", + "input": "[reference(format('publicIPPrefixes[{0}]', copyIndex('value'))).outputs.resourceId.value]" + } + ] + }, + "providedResourceIds": { + "value": "[parameters('publicIPPrefixResourceIds')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "15040145942768763519" + } + }, + "parameters": { + "generatedResourceIds": { + "type": "array", + "defaultValue": [] + }, + "providedResourceIds": { + "type": "array", + "defaultValue": [] + } + }, + "resources": [], + "outputs": { + "formattedResourceIds": { + "type": "array", + "copy": { + "count": "[length(concat(parameters('generatedResourceIds'), parameters('providedResourceIds')))]", + "input": { + "id": "[concat(parameters('generatedResourceIds'), parameters('providedResourceIds'))[copyIndex()]]" + } + } + } + } + } + }, + "dependsOn": [ + "publicIPPrefixes" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the NAT Gateway." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the NAT Gateway." + }, + "value": "[resourceId('Microsoft.Network/natGateways', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the NAT Gateway was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('natGateway', '2023-04-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "natGatewayPublicIp" + ] + }, + "buildImagesRoleAssignment": { + "copy": { + "name": "buildImagesRoleAssignment", + "count": "[length(parameters('computeTypes'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('buildImagesRoleAssignment-{0}-{1}', uniqueString(resourceGroup().id), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[reference(format('buildImages[{0}]', copyIndex()), '2019-06-01-preview', 'full').identity.principalId]" + }, + "resourceId": { + "value": "[reference('acr').outputs.resourceId.value]" + }, + "roleDefinitionId": { + "value": "8311e382-0749-4cb8-b61a-304f252e45ec" + }, + "principalType": { + "value": "ServicePrincipal" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "1847432691715853168" + }, + "name": "Resource-scoped role assignment", + "description": "This module deploys a Role Assignment for a specific resource.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The scope for the role assignment, fully qualified resourceId." + } + }, + "name": { + "type": "string", + "defaultValue": "[guid(parameters('resourceId'), parameters('principalId'), if(contains(parameters('roleDefinitionId'), '/providers/Microsoft.Authorization/roleDefinitions/'), parameters('roleDefinitionId'), subscriptionResourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))))]", + "metadata": { + "description": "Optional. The unique guid name for the role assignment." + } + }, + "roleDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. The role definition ID for the role assignment." + } + }, + "roleName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The name for the role, used for logging." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The Principal or Object ID of the Security Principal (User, Group, Service Principal, Managed Identity)." + } + }, + "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 role assignment." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "$fxv#0": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "scope": { + "type": "string" + }, + "name": { + "type": "string" + }, + "roleDefinitionId": { + "type": "string" + }, + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User", + "" + ], + "defaultValue": "", + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[[parameters('scope')]", + "name": "[[parameters('name')]", + "properties": { + "roleDefinitionId": "[[parameters('roleDefinitionId')]", + "principalId": "[[parameters('principalId')]", + "principalType": "[[parameters('principalType')]", + "description": "[[parameters('description')]" + } + } + ], + "outputs": { + "roleAssignmentId": { + "type": "string", + "value": "[[extensionResourceId(parameters('scope'), 'Microsoft.Authorization/roleAssignments', parameters('name'))]" + } + } + } + }, + "resources": [ + { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.ptn.authorization-resourceroleassignment.{0}.{1}', replace('0.1.1', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('{0}-ResourceRoleAssignment', guid(parameters('resourceId'), parameters('principalId'), parameters('roleDefinitionId')))]", + "properties": { + "mode": "Incremental", + "expressionEvaluationOptions": { + "scope": "Outer" + }, + "template": "[variables('$fxv#0')]", + "parameters": { + "scope": { + "value": "[parameters('resourceId')]" + }, + "name": { + "value": "[parameters('name')]" + }, + "roleDefinitionId": { + "value": "[if(contains(parameters('roleDefinitionId'), '/providers/Microsoft.Authorization/roleDefinitions/'), parameters('roleDefinitionId'), subscriptionResourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId')))]" + }, + "principalId": { + "value": "[parameters('principalId')]" + }, + "principalType": { + "value": "[parameters('principalType')]" + }, + "description": { + "value": "[parameters('description')]" + } + } + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The GUID of the Role Assignment." + }, + "value": "[parameters('name')]" + }, + "roleName": { + "type": "string", + "metadata": { + "description": "The name for the role, used for logging." + }, + "value": "[parameters('roleName')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Role Assignment." + }, + "value": "[reference(resourceId('Microsoft.Resources/deployments', format('{0}-ResourceRoleAssignment', guid(parameters('resourceId'), parameters('principalId'), parameters('roleDefinitionId')))), '2023-07-01').outputs.roleAssignmentId.value]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the role assignment was applied at." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "acr", + "[format('buildImages[{0}]', copyIndex())]" + ] + }, + "aciJob": { + "copy": { + "name": "aciJob", + "count": "[length(range(0, int(coalesce(tryGet(tryGet(parameters('selfHostedConfig'), 'azureContainerInstanceTarget'), 'numberOfInstances'), 1))))]" + }, + "condition": "[contains(parameters('computeTypes'), 'azure-container-instance')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}aciJob-{1}', parameters('namingPrefix'), range(0, int(coalesce(tryGet(tryGet(parameters('selfHostedConfig'), 'azureContainerInstanceTarget'), 'numberOfInstances'), 1)))[copyIndex()])]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('{0}-{1}-instance-{2}', parameters('namingPrefix'), uniqueString(resourceGroup().id), range(0, int(coalesce(tryGet(tryGet(parameters('selfHostedConfig'), 'azureContainerInstanceTarget'), 'numberOfInstances'), 1)))[copyIndex()])]" + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "[reference('userAssignedIdentity').outputs.resourceId.value]" + ] + } + }, + "imageRegistryCredentials": { + "value": [ + { + "identity": "[reference('userAssignedIdentity').outputs.resourceId.value]", + "server": "[reference('acr').outputs.loginServer.value]" + } + ] + }, + "subnetId": "[if(and(parameters('privateNetworking'), equals(parameters('networkingConfiguration').networkType, 'createNew')), createObject('value', filter(reference('newVnet').outputs.subnetResourceIds.value, lambda('subnetId', contains(lambdaVariables('subnetId'), coalesce(tryGet(parameters('networkingConfiguration'), 'containerInstanceSubnetName'), 'aci-subnet'))))[0]), if(and(parameters('privateNetworking'), equals(parameters('networkingConfiguration').networkType, 'useExisting')), createObject('value', format('{0}/subnets/{1}', parameters('networkingConfiguration').virtualNetworkResourceId, parameters('networkingConfiguration').computeNetworking.containerInstanceSubnetName)), createObject('value', null())))]", + "ipAddressType": "[if(parameters('privateNetworking'), createObject('value', 'Private'), createObject('value', 'Public'))]", + "sku": { + "value": "Standard" + }, + "containers": { + "value": [ + { + "name": "[if(equals(parameters('selfHostedConfig').selfHostedType, 'github'), format('private-runner-github-aci-{0}', range(0, int(coalesce(tryGet(tryGet(parameters('selfHostedConfig'), 'azureContainerInstanceTarget'), 'numberOfInstances'), 1)))[copyIndex()]), if(equals(parameters('selfHostedConfig').selfHostedType, 'azuredevops'), format('private-runner-devops-aci-{0}', range(0, int(coalesce(tryGet(tryGet(parameters('selfHostedConfig'), 'azureContainerInstanceTarget'), 'numberOfInstances'), 1)))[copyIndex()]), format('private-runner-aci-{0}', range(0, int(coalesce(tryGet(tryGet(parameters('selfHostedConfig'), 'azureContainerInstanceTarget'), 'numberOfInstances'), 1)))[copyIndex()])))]", + "properties": { + "image": "[format('{0}/{1}-azure-container-instance:latest', reference('acr').outputs.loginServer.value, parameters('selfHostedConfig').selfHostedType)]", + "ports": [ + { + "port": 80, + "protocol": "TCP" + } + ], + "resources": { + "requests": { + "cpu": "[coalesce(tryGet(parameters('selfHostedConfig'), 'cpu'), 1)]", + "memoryInGB": "[coalesce(tryGet(parameters('selfHostedConfig'), 'memoryInGB'), 2)]" + } + }, + "environmentVariables": "[if(equals(parameters('selfHostedConfig').selfHostedType, 'github'), createArray(createObject('name', 'GH_RUNNER_NAME', 'value', coalesce(tryGet(parameters('selfHostedConfig'), 'runnerName'), format('runner-{0}', range(0, int(coalesce(tryGet(tryGet(parameters('selfHostedConfig'), 'azureContainerInstanceTarget'), 'numberOfInstances'), 1)))[copyIndex()]))), createObject('name', 'GH_RUNNER_URL', 'value', variables('gitHubRunnerURL')), createObject('name', 'GH_RUNNER_GROUP', 'value', if(not(equals(tryGet(parameters('selfHostedConfig'), 'runnerGroup'), null())), format('{0}', tryGet(parameters('selfHostedConfig'), 'runnerGroup')), '')), createObject('name', 'GH_RUNNER_TOKEN', 'secureValue', parameters('selfHostedConfig').personalAccessToken)), createArray(createObject('name', 'AZP_URL', 'value', variables('devOpsOrgURL')), createObject('name', 'AZP_POOL', 'value', parameters('selfHostedConfig').agentsPoolName), createObject('name', 'AZP_AGENT_NAME', 'value', coalesce(tryGet(parameters('selfHostedConfig'), 'agentName'), format('agent-{0}', range(0, int(coalesce(tryGet(tryGet(parameters('selfHostedConfig'), 'azureContainerInstanceTarget'), 'numberOfInstances'), 1)))[copyIndex()]))), createObject('name', 'AZP_TOKEN', 'secureValue', parameters('selfHostedConfig').personalAccessToken)))]" + } + } + ] + }, + "ipAddressPorts": { + "value": [ + { + "port": 80, + "protocol": "TCP" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.54.24096", + "templateHash": "15213287633485317123" + }, + "name": "Container Instances Container Groups", + "description": "This module deploys a Container Instance Container Group.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true + }, + "containerType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the container instance." + } + }, + "properties": { + "type": "object", + "properties": { + "image": { + "type": "string", + "metadata": { + "description": "Required. The name of the container source image." + } + }, + "livenessProbe": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The liveness probe." + } + }, + "ports": { + "type": "array", + "items": { + "type": "object", + "properties": { + "port": { + "type": "int", + "metadata": { + "description": "Required. The port number exposed on the container instance." + } + }, + "protocol": { + "type": "string", + "metadata": { + "description": "Required. The protocol associated with the port number." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The exposed ports on the container instance." + } + }, + "resources": { + "type": "object", + "properties": { + "requests": { + "type": "object", + "properties": { + "cpu": { + "type": "int", + "metadata": { + "description": "Required. The CPU request of this container instance." + } + }, + "gpu": { + "$ref": "#/definitions/containerGpuType", + "nullable": true, + "metadata": { + "description": "Optional. The GPU request of this container instance." + } + }, + "memoryInGB": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The memory request in GB of this container instance. To specify a decimal value, use the json() function." + } + } + }, + "metadata": { + "description": "Required. The resource requests of this container instance." + } + }, + "limits": { + "type": "object", + "properties": { + "cpu": { + "type": "int", + "metadata": { + "description": "Required. The CPU limit of this container instance." + } + }, + "gpu": { + "$ref": "#/definitions/containerGpuType", + "nullable": true, + "metadata": { + "description": "Optional. The GPU limit of this container instance." + } + }, + "memoryInGB": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The memory limit in GB of this container instance. To specify a decimal value, use the json() function." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource limits of this container instance." + } + }, + "securityContext": { + "type": "object", + "properties": { + "allowPrivilegeEscalation": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether privilege escalation is allowed for the container." + } + }, + "capabilities": { + "type": "object", + "properties": { + "add": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of capabilities to add." + } + }, + "drop": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of capabilities to drop." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The capabilities to add or drop for the container." + } + }, + "privileged": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Whether the container is run in privileged mode." + } + }, + "runAsGroup": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The GID to run the container as." + } + }, + "runAsUser": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The UID to run the container as." + } + }, + "seccompProfile": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The seccomp profile to use for the container." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The security context of the container instance." + } + } + }, + "metadata": { + "description": "Required. The resource requirements of the container instance." + } + }, + "volumeMounts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the volume mount." + } + }, + "mountPath": { + "type": "string", + "metadata": { + "description": "Required. The path within the container where the volume should be mounted. Must not contain colon (:)." + } + }, + "readOnly": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The flag indicating whether the volume mount is read-only." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The volume mounts within the container instance." + } + }, + "command": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The command to execute within the container instance." + } + }, + "environmentVariables": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the environment variable." + } + }, + "secureValue": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The value of the secure environment variable." + } + }, + "value": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The value of the environment variable." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The environment variables to set in the container instance." + } + } + }, + "metadata": { + "description": "Required. The properties of the container instance." + } + } + } + } + }, + "ipAddressPortsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "port": { + "type": "int", + "metadata": { + "description": "Required. The port number exposed on the container instance." + } + }, + "protocol": { + "type": "string", + "metadata": { + "description": "Required. The protocol associated with the port number." + } + } + } + } + }, + "containerGpuType": { + "type": "object", + "properties": { + "count": { + "type": "int", + "metadata": { + "description": "Required. The count of the GPU resource." + } + }, + "sku": { + "type": "string", + "allowedValues": [ + "K80", + "P100", + "V100" + ], + "metadata": { + "description": "Required. The SKU of the GPU resource." + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name for the container group." + } + }, + "containers": { + "$ref": "#/definitions/containerType", + "metadata": { + "description": "Required. The containers and their respective config within the container group." + } + }, + "ipAddressPorts": { + "$ref": "#/definitions/ipAddressPortsType", + "metadata": { + "description": "Conditional. Ports to open on the public IP address. Must include all ports assigned on container level. Required if `ipAddressType` is set to `public`." + } + }, + "osType": { + "type": "string", + "defaultValue": "Linux", + "metadata": { + "description": "Optional. The operating system type required by the containers in the container group. - Windows or Linux." + } + }, + "restartPolicy": { + "type": "string", + "defaultValue": "Always", + "allowedValues": [ + "Always", + "OnFailure", + "Never" + ], + "metadata": { + "description": "Optional. Restart policy for all containers within the container group. - Always: Always restart. OnFailure: Restart on failure. Never: Never restart. - Always, OnFailure, Never." + } + }, + "ipAddressType": { + "type": "string", + "defaultValue": "Public", + "allowedValues": [ + "Public", + "Private" + ], + "metadata": { + "description": "Optional. Specifies if the IP is exposed to the public internet or private VNET. - Public or Private." + } + }, + "imageRegistryCredentials": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The image registry credentials by which the container group is created from." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "autoGeneratedDomainNameLabelScope": { + "type": "string", + "defaultValue": "TenantReuse", + "allowedValues": [ + "Noreuse", + "ResourceGroupReuse", + "SubscriptionReuse", + "TenantReuse", + "Unsecure" + ], + "metadata": { + "description": "Optional. Specify level of protection of the domain name label." + } + }, + "dnsNameLabel": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Dns name label for the resource." + } + }, + "dnsNameServers": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. List of dns servers used by the containers for lookups." + } + }, + "dnsSearchDomains": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. DNS search domain which will be appended to each DNS lookup." + } + }, + "initContainers": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. A list of container definitions which will be executed before the application container starts." + } + }, + "subnetId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the subnet. Only specify when ipAddressType is Private." + } + }, + "volumes": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Specify if volumes (emptyDir, AzureFileShare or GitRepo) shall be attached to your containergroup." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "sku": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Dedicated", + "Standard" + ], + "metadata": { + "description": "Optional. The container group SKU." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "metadata": { + "description": "Optional. The customer managed key definition." + } + } + }, + "variables": { + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned, UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]" + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", + "dependsOn": [ + "cMKKeyVault" + ] + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.containerinstance-containergroup.{0}.{1}', replace('0.2.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + }, + "containergroup": { + "type": "Microsoft.ContainerInstance/containerGroups", + "apiVersion": "2023-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "properties": "[union(createObject('containers', parameters('containers'), 'encryptionProperties', if(not(empty(parameters('customerManagedKey'))), createObject('identity', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), ''))), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))), null()), 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))), 'vaultBaseUrl', reference('cMKKeyVault').vaultUri), null()), 'imageRegistryCredentials', parameters('imageRegistryCredentials'), 'initContainers', parameters('initContainers'), 'restartPolicy', parameters('restartPolicy'), 'osType', parameters('osType'), 'ipAddress', createObject('type', parameters('ipAddressType'), 'autoGeneratedDomainNameLabelScope', if(not(empty(parameters('dnsNameServers'))), parameters('autoGeneratedDomainNameLabelScope'), null()), 'dnsNameLabel', parameters('dnsNameLabel'), 'ports', parameters('ipAddressPorts')), 'sku', parameters('sku'), 'subnetIds', if(not(empty(parameters('subnetId'))), createArray(createObject('id', parameters('subnetId'))), null()), 'volumes', parameters('volumes')), if(not(empty(parameters('dnsNameServers'))), createObject('dnsConfig', createObject('nameServers', parameters('dnsNameServers'), 'searchDomains', parameters('dnsSearchDomains'))), createObject()))]", + "dependsOn": [ + "cMKKeyVault", + "cMKUserAssignedIdentity" + ] + }, + "containergroup_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.ContainerInstance/containerGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "containergroup" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the container group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the container group." + }, + "value": "[resourceId('Microsoft.ContainerInstance/containerGroups', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the container group was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "iPv4Address": { + "type": "string", + "metadata": { + "description": "The IPv4 address of the container group." + }, + "value": "[reference('containergroup').ipAddress.ip]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('containergroup', '2023-05-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('containergroup', '2023-05-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "acr", + "newVnet", + "taskRun", + "userAssignedIdentity" + ] + }, + "acaJob": { + "condition": "[contains(parameters('computeTypes'), 'azure-container-app')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}acaJob', parameters('namingPrefix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('{0}-{1}-acajob', parameters('namingPrefix'), uniqueString(resourceGroup().id))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "[reference('userAssignedIdentity').outputs.resourceId.value]" + ] + } + }, + "roleAssignments": { + "value": [ + { + "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]", + "roleDefinitionIdOrName": "Contributor", + "principalType": "ServicePrincipal", + "name": "[guid(reference('userAssignedIdentity').outputs.principalId.value, 'Contributor', resourceGroup().id)]" + } + ] + }, + "triggerType": { + "value": "Event" + }, + "replicaRetryLimit": { + "value": 3 + }, + "replicaTimeout": { + "value": 1800 + }, + "eventTriggerConfig": { + "value": { + "parallelism": 1, + "replicaCompletionCount": 1, + "scale": { + "maxExecutions": 100, + "minExecutions": 0, + "pollingInterval": 30, + "rules": "[if(equals(parameters('selfHostedConfig').selfHostedType, 'github'), variables('acaGitHubRules'), variables('acaAzureDevOpsRules'))]" + } + } + }, + "registries": { + "value": [ + { + "server": "[reference('acr').outputs.loginServer.value]", + "identity": "[reference('userAssignedIdentity').outputs.resourceId.value]" + } + ] + }, + "secrets": "[if(equals(parameters('selfHostedConfig').selfHostedType, 'github'), createObject('value', variables('acaGitHubSecrets')), createObject('value', variables('acaAzureDevOpsSecrets')))]", + "containers": { + "value": [ + { + "image": "[format('{0}/{1}-azure-container-app:latest', reference('acr').outputs.loginServer.value, parameters('selfHostedConfig').selfHostedType)]", + "name": "[format('private-runner-{0}-aca', parameters('selfHostedConfig').selfHostedType)]", + "resources": "[coalesce(tryGet(tryGet(parameters('selfHostedConfig'), 'azureContainerAppTarget'), 'resources'), createObject('cpu', '1', 'memory', '2Gi'))]", + "env": "[if(equals(parameters('selfHostedConfig').selfHostedType, 'github'), variables('acaGitHubEnvVariables'), variables('acaAzureDevOpsEnvVariables'))]" + } + ] + }, + "environmentResourceId": { + "value": "[reference('appEnvironment').outputs.resourceId.value]" + }, + "workloadProfileName": { + "value": "consumption" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "15263623786669006643" + }, + "name": "Container App Jobs", + "description": "This module deploys a Container App Job.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + } + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + } + }, + "registryType": { + "type": "object", + "properties": { + "server": { + "type": "string", + "metadata": { + "example": "myregistry.azurecr.io", + "description": "Required. The FQDN name of the container registry." + } + }, + "identity": { + "type": "string", + "nullable": true, + "metadata": { + "example": " user-assigned identity: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity\n system-assigned identity: system\n ", + "description": "Optional. The resource ID of the (user) managed identity, which is used to access the Azure Container Registry." + } + }, + "username": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The username for the container registry." + } + }, + "passwordSecretRef": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The name of the secret contains the login password. Required if `username` is not null." + } + } + } + }, + "secretType": { + "type": "object", + "properties": { + "identity": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of a managed identity to authenticate with Azure Key Vault, or System to use a system-assigned identity." + } + }, + "keyVaultUrl": { + "type": "string", + "nullable": true, + "metadata": { + "example": "https://myvault${environment().suffixes.keyvaultDns}/secrets/mysecret", + "description": "Conditional. Azure Key Vault URL pointing to the secret referenced by the Container App Job. Required if `value` is null." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the secret." + } + }, + "value": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Conditional. The secret value, if not fetched from Key Vault. Required if `keyVaultUrl` is not null." + } + } + } + }, + "volumeType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the volume." + } + }, + "mountOptions": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Mount options used while mounting the Azure file share or NFS Azure file share. Must be a comma-separated string. Required if `storageType` is not `EmptyDir`." + } + }, + "secrets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string", + "metadata": { + "description": "Required. Path to project secret to. If no path is provided, path defaults to name of secret listed in secretRef." + } + }, + "secretRef": { + "type": "string", + "metadata": { + "description": "Required. Name of the Container App secret from which to pull the secret value." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. List of secrets to be added in volume. If no secrets are provided, all secrets in collection will be added to volume." + } + }, + "storageName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The storage account name. Not needed for EmptyDir and Secret. Required if `storageType` is `AzureFile` or `NfsAzureFile`." + } + }, + "storageType": { + "type": "string", + "allowedValues": [ + "AzureFile", + "EmptyDir", + "NfsAzureFile", + "Secret" + ], + "metadata": { + "description": "Required. The container name." + } + } + } + }, + "containerEnvironmentVariablesType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The environment variable name." + } + }, + "secretRef": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The name of the Container App secret from which to pull the envrionment variable value. Required if `value` is null." + } + }, + "value": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The environment variable value. Required if `secretRef` is null." + } + } + } + }, + "containerProbeType": { + "type": "object", + "properties": { + "failureThreshold": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 10, + "metadata": { + "description": "Optional. Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3." + } + }, + "httpGet": { + "type": "object", + "properties": { + "host": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Host name to connect to, defaults to the pod IP." + } + }, + "httpHeaders": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The header field name." + } + }, + "value": { + "type": "string", + "metadata": { + "description": "Required. The header field value." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom headers to set in the request." + } + }, + "path": { + "type": "string", + "metadata": { + "description": "Required. Path to access on the HTTP server." + } + }, + "port": { + "type": "int", + "minValue": 1, + "maxValue": 65535, + "metadata": { + "description": "Required. Name of the port to access on the container. If not specified, the containerPort is used." + } + }, + "scheme": { + "type": "string", + "allowedValues": [ + "HTTP", + "HTTPS" + ], + "nullable": true, + "metadata": { + "description": "Required. Scheme to use for connecting to the host. Defaults to HTTP." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. HTTPGet specifies the http request to perform." + } + }, + "initialDelaySeconds": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 60, + "metadata": { + "description": "Optional. Number of seconds after the container has started before liveness probes are initiated. Defaults to 0 seconds." + } + }, + "periodSeconds": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 60, + "metadata": { + "description": "Optional. How often (in seconds) to perform the probe. Defaults to 10 seconds." + } + }, + "successThreshold": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 10, + "metadata": { + "description": "Optional. Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1." + } + }, + "tcpSocket": { + "type": "object", + "properties": { + "host": { + "type": "string", + "metadata": { + "description": "Optional. Host name to connect to, defaults to the pod IP." + } + }, + "port": { + "type": "int", + "minValue": 1, + "maxValue": 65535, + "metadata": { + "description": "Required. Name of the port to access on the container. If not specified, the containerPort is used." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. TCPSocket specifies an action involving a TCP port." + } + }, + "terminationGracePeriodSeconds": { + "type": "int", + "nullable": true, + "minValue": 0, + "maxValue": 3600, + "metadata": { + "description": "Optional. Duration in seconds the pod needs to terminate gracefully upon probe failure. This is an alpha field and requires enabling ProbeTerminationGracePeriod feature gate." + } + }, + "timeoutSeconds": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 240, + "metadata": { + "description": "Optional. Number of seconds after which the probe times out. Defaults to 1 second." + } + }, + "type": { + "type": "string", + "allowedValues": [ + "Liveness", + "Readiness", + "Startup" + ], + "metadata": { + "description": "Required. The type of probe." + } + } + } + }, + "containerResourceType": { + "type": "object", + "properties": { + "cpu": { + "type": "string", + "metadata": { + "example": " '0.25'\n '1'\n ", + "description": "Required. The CPU limit of the container in cores." + } + }, + "memory": { + "type": "string", + "metadata": { + "example": " '250Mb'\n '1.5Gi'\n '1500Mi'\n ", + "description": "Optional. The required memory." + } + } + } + }, + "containerVolumeMountType": { + "type": "object", + "properties": { + "mountPath": { + "type": "string", + "metadata": { + "description": "Required. The path within the container at which the volume should be mounted. Must not contain ':'." + } + }, + "subPath": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Path within the volume from which the container's volume should be mounted." + } + }, + "volumeName": { + "type": "string", + "metadata": { + "description": "Required. This must match the Name of a Volume." + } + } + } + }, + "manualTriggerConfigType": { + "type": "object", + "properties": { + "parallelism": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Number of parallel replicas of a job that can run at a given time. Defaults to 1." + } + }, + "replicaCompletionCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Minimum number of successful replica completions before overall job completion. Must be equal or or less than the parallelism. Defaults to 1." + } + } + } + }, + "scheduleTriggerconfigType": { + "type": "object", + "properties": { + "cronExpression": { + "type": "string", + "metadata": { + "example": " '* * * * *' // Every minute, every hour, every day\n '0 0 * * *' // at 00:00 UTC every day\n ", + "description": "Required. Cron formatted repeating schedule (\"* * * * *\") of a Cron Job. It supports the standard [cron](https://en.wikipedia.org/wiki/Cron) expression syntax." + } + }, + "parallelism": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Number of parallel replicas of a job that can run at a given time. Defaults to 1." + } + }, + "replicaCompletionCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Number of successful completions of a job that are necessary to consider the job complete. Must be equal or or less than the parallelism. Defaults to 1." + } + } + } + }, + "eventTriggerConfigType": { + "type": "object", + "properties": { + "parallelism": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Number of parallel replicas of a job that can run at a given time. Defaults to 1." + } + }, + "replicaCompletionCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Minimum number of successful replica completions before overall job completion. Must be equal or or less than the parallelism. Defaults to 1." + } + }, + "scale": { + "$ref": "#/definitions/jobScaleType", + "metadata": { + "description": "Required. Scaling configurations for event driven jobs." + } + } + } + }, + "jobScaleType": { + "type": "object", + "properties": { + "maxExecutions": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Maximum number of job executions that are created for a trigger, default 100." + } + }, + "minExecutions": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Minimum number of job executions that are created for a trigger, default 0." + } + }, + "pollingInterval": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Interval to check each event source in seconds. Defaults to 30s." + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "auth": { + "type": "array", + "items": { + "type": "object", + "properties": { + "secretRef": { + "type": "string", + "metadata": { + "description": "Required. Name of the secret from which to pull the auth params." + } + }, + "triggerParameter": { + "type": "string", + "metadata": { + "description": "Required. Trigger Parameter that uses the secret." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Required. Authentication secrets for the scale rule." + } + }, + "metadata": { + "type": "object", + "metadata": { + "example": " {\n \"// for type azure-queue\n {\n queueName: 'default'\n storageAccountResourceId: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/myStorageAccount'\n }\"\n }\n ", + "description": "Optional. Metadata properties to describe the scale rule." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the scale rule." + } + }, + "type": { + "type": "string", + "metadata": { + "example": " \"azure-servicebus\"\n \"azure-queue\"\n \"redis\"\n ", + "description": "Optional. The type of the rule." + } + } + } + }, + "metadata": { + "example": " [\n // for type azure-queue\n {\n name: 'myrule'\n type: 'azure-queue'\n metadata: {\n queueName: 'default'\n storageAccountResourceId: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/myStorageAccount'\n }\n auth: {\n secretRef: 'mysecret'\n triggerParameter: 'queueName'\n }\n }\n ]\n ", + "description": "Optional. Scaling rules for the job." + } + } + } + }, + "initContainerType": { + "type": "object", + "properties": { + "args": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Optional. Container start command arguments." + } + }, + "command": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Optional. Container start command." + } + }, + "env": { + "type": "array", + "items": { + "$ref": "#/definitions/containerEnvironmentVariablesType" + }, + "nullable": true, + "metadata": { + "example": " [\n {\n name: 'AZURE_STORAGE_QUEUE_NAME'\n value: ''\n }\n {\n name: 'AZURE_STORAGE_CONNECTION_STRING'\n secretRef: 'connection-string'\n }\n ]\n ", + "description": "Optional. The environment variables to set in the container." + } + }, + "image": { + "type": "string", + "metadata": { + "description": "Required. The image of the container." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the container." + } + }, + "resources": { + "$ref": "#/definitions/containerResourceType", + "nullable": true, + "metadata": { + "description": "Required. Container resource requirements." + } + }, + "volumeMounts": { + "type": "array", + "items": { + "$ref": "#/definitions/containerVolumeMountType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The volume mounts to attach to the container." + } + } + } + }, + "containerType": { + "type": "object", + "properties": { + "args": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Container start command arguments." + } + }, + "command": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The command to run in the container." + } + }, + "env": { + "type": "array", + "items": { + "$ref": "#/definitions/containerEnvironmentVariablesType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The environment variables to set in the container." + } + }, + "image": { + "type": "string", + "metadata": { + "description": "Required. The image of the container." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the container." + } + }, + "probes": { + "type": "array", + "items": { + "$ref": "#/definitions/containerProbeType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The probes of the container." + } + }, + "resources": { + "$ref": "#/definitions/containerResourceType", + "nullable": true, + "metadata": { + "description": "Optional. The resources to allocate to the container." + } + }, + "volumeMounts": { + "type": "array", + "items": { + "$ref": "#/definitions/containerVolumeMountType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The volume mounts to attach to the container." + } + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Container App." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources. Defaults to the location of the Resource Group." + } + }, + "environmentResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of Container Apps Environment." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "example": " {\n key1: 'value1'\n key2: 'value2'\n }\n ", + "description": "Optional. Tags of the resource." + } + }, + "registries": { + "type": "array", + "items": { + "$ref": "#/definitions/registryType" + }, + "nullable": true, + "metadata": { + "example": " [\n {\n server: 'myregistry.azurecr.io'\n identity: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity'\n }\n {\n server: 'myregistry2.azurecr.io'\n identity: 'system'\n }\n {\n server: 'myregistry3.azurecr.io'\n username: 'myusername'\n passwordSecretRef: 'secret-name'\n }\n ]", + "description": "Optional. Collection of private container registry credentials for containers used by the Container app." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "nullable": true, + "metadata": { + "example": " {\n systemAssigned: true,\n userAssignedResourceIds: [\n '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity'\n ]\n }\n {\n systemAssigned: true\n }\n ", + "description": "Optional. The managed identity definition for this resource." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "containers": { + "type": "array", + "items": { + "$ref": "#/definitions/containerType" + }, + "metadata": { + "description": "Required. List of container definitions for the Container App." + } + }, + "initContainers": { + "type": "array", + "items": { + "$ref": "#/definitions/initContainerType" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of specialized containers that run before app containers." + } + }, + "eventTriggerConfig": { + "$ref": "#/definitions/eventTriggerConfigType", + "nullable": true, + "metadata": { + "description": "Conditional. Configuration of an event driven job. Required if `TriggerType` is `Event`." + } + }, + "scheduleTriggerConfig": { + "$ref": "#/definitions/scheduleTriggerconfigType", + "nullable": true, + "metadata": { + "description": "Conditional. Configuration of a schedule based job. Required if `TriggerType` is `Schedule`." + } + }, + "manualTriggerConfig": { + "$ref": "#/definitions/manualTriggerConfigType", + "nullable": true, + "metadata": { + "description": "Conditional. Configuration of a manually triggered job. Required if `TriggerType` is `Manual`." + } + }, + "replicaRetryLimit": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Optional. The maximum number of times a replica can be retried." + } + }, + "workloadProfileName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the workload profile to use. Leave empty to use a consumption based profile." + } + }, + "secrets": { + "type": "array", + "items": { + "$ref": "#/definitions/secretType" + }, + "nullable": true, + "metadata": { + "example": " [\n {\n name: 'mysecret'\n identity: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity'\n keyVaultUrl: 'https://myvault${environment().suffixes.keyvaultDns}/secrets/mysecret'\n }\n {\n name: 'mysecret'\n identity: 'system'\n keyVaultUrl: 'https://myvault${environment().suffixes.keyvaultDns}/secrets/mysecret'\n }\n {\n // You can do this, but you shouldn't. Use a secret reference instead.\n name: 'mysecret'\n value: 'mysecretvalue'\n }\n {\n name: 'connection-string'\n value: listKeys('/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/myStorageAccount', '2023-04-01').keys[0].value\n }\n ]\n ", + "description": "Optional. The secrets of the Container App." + } + }, + "volumes": { + "type": "array", + "items": { + "$ref": "#/definitions/volumeType" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of volume definitions for the Container App." + } + }, + "replicaTimeout": { + "type": "int", + "defaultValue": 1800, + "metadata": { + "description": "Optional. Maximum number of seconds a replica is allowed to run." + } + }, + "triggerType": { + "type": "string", + "allowedValues": [ + "Event", + "Manual", + "Schedule" + ], + "metadata": { + "description": "Required. Trigger type of the job." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.app-job.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "job": { + "type": "Microsoft.App/jobs", + "apiVersion": "2024-03-01", + "name": "[parameters('name')]", + "tags": "[parameters('tags')]", + "location": "[parameters('location')]", + "identity": "[variables('identity')]", + "properties": { + "environmentId": "[parameters('environmentResourceId')]", + "configuration": { + "triggerType": "[parameters('triggerType')]", + "eventTriggerConfig": "[if(equals(parameters('triggerType'), 'Event'), parameters('eventTriggerConfig'), null())]", + "manualTriggerConfig": "[if(equals(parameters('triggerType'), 'Manual'), parameters('manualTriggerConfig'), null())]", + "scheduleTriggerConfig": "[if(equals(parameters('triggerType'), 'Schedule'), parameters('scheduleTriggerConfig'), null())]", + "replicaRetryLimit": "[parameters('replicaRetryLimit')]", + "replicaTimeout": "[parameters('replicaTimeout')]", + "registries": "[parameters('registries')]", + "secrets": "[parameters('secrets')]" + }, + "template": { + "containers": "[parameters('containers')]", + "initContainers": "[parameters('initContainers')]", + "volumes": "[parameters('volumes')]" + }, + "workloadProfileName": "[parameters('workloadProfileName')]" + } + }, + "job_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.App/jobs/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "job" + ] + }, + "job_roleAssignments": { + "copy": { + "name": "job_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.App/jobs/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.App/jobs', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "job" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "example": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.App/jobs/myJob", + "description": "The resource ID of the Container App Job." + }, + "value": "[resourceId('Microsoft.App/jobs', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "example": "myResourceGroup", + "description": "The name of the resource group the Container App Job was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "example": "myJob", + "description": "The name of the Container App Job." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "example": "Germany West Central", + "description": "The location the resource was deployed into." + }, + "value": "[reference('job', '2024-03-01', 'full').location]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "example": "00000000-0000-0000-0000-000000000000", + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('job', '2024-03-01', 'full'), 'identity'), 'principalId'), '')]" + } + } + } + }, + "dependsOn": [ + "acr", + "appEnvironment", + "runPlaceHolderAgent", + "taskRun", + "userAssignedIdentity" + ] + }, + "acaPlaceholderJob": { + "condition": "[and(contains(parameters('computeTypes'), 'azure-container-app'), equals(parameters('selfHostedConfig').selfHostedType, 'azuredevops'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "acaDevOpsPlaceholderJob", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('{0}-{1}-placeholder', parameters('namingPrefix'), uniqueString(resourceGroup().id))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "[reference('userAssignedIdentity').outputs.resourceId.value]" + ] + } + }, + "roleAssignments": { + "value": [ + { + "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]", + "roleDefinitionIdOrName": "Contributor", + "principalType": "ServicePrincipal" + } + ] + }, + "triggerType": { + "value": "Manual" + }, + "replicaRetryLimit": { + "value": 0 + }, + "replicaTimeout": { + "value": 300 + }, + "manualTriggerConfig": { + "value": { + "parallelism": 1, + "replicaCompletionCount": 1 + } + }, + "registries": { + "value": [ + { + "server": "[reference('acr').outputs.loginServer.value]", + "identity": "[reference('userAssignedIdentity').outputs.resourceId.value]" + } + ] + }, + "secrets": { + "value": "[variables('acaAzureDevOpsSecrets')]" + }, + "containers": { + "value": [ + { + "image": "[format('{0}/{1}-azure-container-app:latest', reference('acr').outputs.loginServer.value, parameters('selfHostedConfig').selfHostedType)]", + "name": "private-runner-devops-aca", + "resources": { + "cpu": "1", + "memory": "2Gi" + }, + "env": [ + { + "name": "AZP_POOL", + "value": "[parameters('selfHostedConfig').agentsPoolName]" + }, + { + "name": "AZP_AGENT_NAME", + "value": "[coalesce(tryGet(parameters('selfHostedConfig'), 'placeHolderAgentName'), 'placeHolderAgent')]" + }, + { + "name": "AZP_PLACEHOLDER", + "value": "1" + }, + { + "name": "AZP_URL", + "secretRef": "organization-url" + }, + { + "name": "AZP_TOKEN", + "secretRef": "personal-access-token" + } + ] + } + ] + }, + "environmentResourceId": { + "value": "[reference('appEnvironment').outputs.resourceId.value]" + }, + "workloadProfileName": { + "value": "consumption" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "15263623786669006643" + }, + "name": "Container App Jobs", + "description": "This module deploys a Container App Job.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + } + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + } + }, + "registryType": { + "type": "object", + "properties": { + "server": { + "type": "string", + "metadata": { + "example": "myregistry.azurecr.io", + "description": "Required. The FQDN name of the container registry." + } + }, + "identity": { + "type": "string", + "nullable": true, + "metadata": { + "example": " user-assigned identity: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity\n system-assigned identity: system\n ", + "description": "Optional. The resource ID of the (user) managed identity, which is used to access the Azure Container Registry." + } + }, + "username": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The username for the container registry." + } + }, + "passwordSecretRef": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The name of the secret contains the login password. Required if `username` is not null." + } + } + } + }, + "secretType": { + "type": "object", + "properties": { + "identity": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of a managed identity to authenticate with Azure Key Vault, or System to use a system-assigned identity." + } + }, + "keyVaultUrl": { + "type": "string", + "nullable": true, + "metadata": { + "example": "https://myvault${environment().suffixes.keyvaultDns}/secrets/mysecret", + "description": "Conditional. Azure Key Vault URL pointing to the secret referenced by the Container App Job. Required if `value` is null." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the secret." + } + }, + "value": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Conditional. The secret value, if not fetched from Key Vault. Required if `keyVaultUrl` is not null." + } + } + } + }, + "volumeType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the volume." + } + }, + "mountOptions": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Mount options used while mounting the Azure file share or NFS Azure file share. Must be a comma-separated string. Required if `storageType` is not `EmptyDir`." + } + }, + "secrets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string", + "metadata": { + "description": "Required. Path to project secret to. If no path is provided, path defaults to name of secret listed in secretRef." + } + }, + "secretRef": { + "type": "string", + "metadata": { + "description": "Required. Name of the Container App secret from which to pull the secret value." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. List of secrets to be added in volume. If no secrets are provided, all secrets in collection will be added to volume." + } + }, + "storageName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The storage account name. Not needed for EmptyDir and Secret. Required if `storageType` is `AzureFile` or `NfsAzureFile`." + } + }, + "storageType": { + "type": "string", + "allowedValues": [ + "AzureFile", + "EmptyDir", + "NfsAzureFile", + "Secret" + ], + "metadata": { + "description": "Required. The container name." + } + } + } + }, + "containerEnvironmentVariablesType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The environment variable name." + } + }, + "secretRef": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The name of the Container App secret from which to pull the envrionment variable value. Required if `value` is null." + } + }, + "value": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The environment variable value. Required if `secretRef` is null." + } + } + } + }, + "containerProbeType": { + "type": "object", + "properties": { + "failureThreshold": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 10, + "metadata": { + "description": "Optional. Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3." + } + }, + "httpGet": { + "type": "object", + "properties": { + "host": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Host name to connect to, defaults to the pod IP." + } + }, + "httpHeaders": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The header field name." + } + }, + "value": { + "type": "string", + "metadata": { + "description": "Required. The header field value." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom headers to set in the request." + } + }, + "path": { + "type": "string", + "metadata": { + "description": "Required. Path to access on the HTTP server." + } + }, + "port": { + "type": "int", + "minValue": 1, + "maxValue": 65535, + "metadata": { + "description": "Required. Name of the port to access on the container. If not specified, the containerPort is used." + } + }, + "scheme": { + "type": "string", + "allowedValues": [ + "HTTP", + "HTTPS" + ], + "nullable": true, + "metadata": { + "description": "Required. Scheme to use for connecting to the host. Defaults to HTTP." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. HTTPGet specifies the http request to perform." + } + }, + "initialDelaySeconds": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 60, + "metadata": { + "description": "Optional. Number of seconds after the container has started before liveness probes are initiated. Defaults to 0 seconds." + } + }, + "periodSeconds": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 60, + "metadata": { + "description": "Optional. How often (in seconds) to perform the probe. Defaults to 10 seconds." + } + }, + "successThreshold": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 10, + "metadata": { + "description": "Optional. Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1." + } + }, + "tcpSocket": { + "type": "object", + "properties": { + "host": { + "type": "string", + "metadata": { + "description": "Optional. Host name to connect to, defaults to the pod IP." + } + }, + "port": { + "type": "int", + "minValue": 1, + "maxValue": 65535, + "metadata": { + "description": "Required. Name of the port to access on the container. If not specified, the containerPort is used." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. TCPSocket specifies an action involving a TCP port." + } + }, + "terminationGracePeriodSeconds": { + "type": "int", + "nullable": true, + "minValue": 0, + "maxValue": 3600, + "metadata": { + "description": "Optional. Duration in seconds the pod needs to terminate gracefully upon probe failure. This is an alpha field and requires enabling ProbeTerminationGracePeriod feature gate." + } + }, + "timeoutSeconds": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 240, + "metadata": { + "description": "Optional. Number of seconds after which the probe times out. Defaults to 1 second." + } + }, + "type": { + "type": "string", + "allowedValues": [ + "Liveness", + "Readiness", + "Startup" + ], + "metadata": { + "description": "Required. The type of probe." + } + } + } + }, + "containerResourceType": { + "type": "object", + "properties": { + "cpu": { + "type": "string", + "metadata": { + "example": " '0.25'\n '1'\n ", + "description": "Required. The CPU limit of the container in cores." + } + }, + "memory": { + "type": "string", + "metadata": { + "example": " '250Mb'\n '1.5Gi'\n '1500Mi'\n ", + "description": "Optional. The required memory." + } + } + } + }, + "containerVolumeMountType": { + "type": "object", + "properties": { + "mountPath": { + "type": "string", + "metadata": { + "description": "Required. The path within the container at which the volume should be mounted. Must not contain ':'." + } + }, + "subPath": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Path within the volume from which the container's volume should be mounted." + } + }, + "volumeName": { + "type": "string", + "metadata": { + "description": "Required. This must match the Name of a Volume." + } + } + } + }, + "manualTriggerConfigType": { + "type": "object", + "properties": { + "parallelism": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Number of parallel replicas of a job that can run at a given time. Defaults to 1." + } + }, + "replicaCompletionCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Minimum number of successful replica completions before overall job completion. Must be equal or or less than the parallelism. Defaults to 1." + } + } + } + }, + "scheduleTriggerconfigType": { + "type": "object", + "properties": { + "cronExpression": { + "type": "string", + "metadata": { + "example": " '* * * * *' // Every minute, every hour, every day\n '0 0 * * *' // at 00:00 UTC every day\n ", + "description": "Required. Cron formatted repeating schedule (\"* * * * *\") of a Cron Job. It supports the standard [cron](https://en.wikipedia.org/wiki/Cron) expression syntax." + } + }, + "parallelism": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Number of parallel replicas of a job that can run at a given time. Defaults to 1." + } + }, + "replicaCompletionCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Number of successful completions of a job that are necessary to consider the job complete. Must be equal or or less than the parallelism. Defaults to 1." + } + } + } + }, + "eventTriggerConfigType": { + "type": "object", + "properties": { + "parallelism": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Number of parallel replicas of a job that can run at a given time. Defaults to 1." + } + }, + "replicaCompletionCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Minimum number of successful replica completions before overall job completion. Must be equal or or less than the parallelism. Defaults to 1." + } + }, + "scale": { + "$ref": "#/definitions/jobScaleType", + "metadata": { + "description": "Required. Scaling configurations for event driven jobs." + } + } + } + }, + "jobScaleType": { + "type": "object", + "properties": { + "maxExecutions": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Maximum number of job executions that are created for a trigger, default 100." + } + }, + "minExecutions": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Minimum number of job executions that are created for a trigger, default 0." + } + }, + "pollingInterval": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Interval to check each event source in seconds. Defaults to 30s." + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "auth": { + "type": "array", + "items": { + "type": "object", + "properties": { + "secretRef": { + "type": "string", + "metadata": { + "description": "Required. Name of the secret from which to pull the auth params." + } + }, + "triggerParameter": { + "type": "string", + "metadata": { + "description": "Required. Trigger Parameter that uses the secret." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Required. Authentication secrets for the scale rule." + } + }, + "metadata": { + "type": "object", + "metadata": { + "example": " {\n \"// for type azure-queue\n {\n queueName: 'default'\n storageAccountResourceId: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/myStorageAccount'\n }\"\n }\n ", + "description": "Optional. Metadata properties to describe the scale rule." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the scale rule." + } + }, + "type": { + "type": "string", + "metadata": { + "example": " \"azure-servicebus\"\n \"azure-queue\"\n \"redis\"\n ", + "description": "Optional. The type of the rule." + } + } + } + }, + "metadata": { + "example": " [\n // for type azure-queue\n {\n name: 'myrule'\n type: 'azure-queue'\n metadata: {\n queueName: 'default'\n storageAccountResourceId: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/myStorageAccount'\n }\n auth: {\n secretRef: 'mysecret'\n triggerParameter: 'queueName'\n }\n }\n ]\n ", + "description": "Optional. Scaling rules for the job." + } + } + } + }, + "initContainerType": { + "type": "object", + "properties": { + "args": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Optional. Container start command arguments." + } + }, + "command": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Optional. Container start command." + } + }, + "env": { + "type": "array", + "items": { + "$ref": "#/definitions/containerEnvironmentVariablesType" + }, + "nullable": true, + "metadata": { + "example": " [\n {\n name: 'AZURE_STORAGE_QUEUE_NAME'\n value: ''\n }\n {\n name: 'AZURE_STORAGE_CONNECTION_STRING'\n secretRef: 'connection-string'\n }\n ]\n ", + "description": "Optional. The environment variables to set in the container." + } + }, + "image": { + "type": "string", + "metadata": { + "description": "Required. The image of the container." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the container." + } + }, + "resources": { + "$ref": "#/definitions/containerResourceType", + "nullable": true, + "metadata": { + "description": "Required. Container resource requirements." + } + }, + "volumeMounts": { + "type": "array", + "items": { + "$ref": "#/definitions/containerVolumeMountType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The volume mounts to attach to the container." + } + } + } + }, + "containerType": { + "type": "object", + "properties": { + "args": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Container start command arguments." + } + }, + "command": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The command to run in the container." + } + }, + "env": { + "type": "array", + "items": { + "$ref": "#/definitions/containerEnvironmentVariablesType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The environment variables to set in the container." + } + }, + "image": { + "type": "string", + "metadata": { + "description": "Required. The image of the container." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the container." + } + }, + "probes": { + "type": "array", + "items": { + "$ref": "#/definitions/containerProbeType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The probes of the container." + } + }, + "resources": { + "$ref": "#/definitions/containerResourceType", + "nullable": true, + "metadata": { + "description": "Optional. The resources to allocate to the container." + } + }, + "volumeMounts": { + "type": "array", + "items": { + "$ref": "#/definitions/containerVolumeMountType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The volume mounts to attach to the container." + } + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Container App." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources. Defaults to the location of the Resource Group." + } + }, + "environmentResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of Container Apps Environment." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "example": " {\n key1: 'value1'\n key2: 'value2'\n }\n ", + "description": "Optional. Tags of the resource." + } + }, + "registries": { + "type": "array", + "items": { + "$ref": "#/definitions/registryType" + }, + "nullable": true, + "metadata": { + "example": " [\n {\n server: 'myregistry.azurecr.io'\n identity: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity'\n }\n {\n server: 'myregistry2.azurecr.io'\n identity: 'system'\n }\n {\n server: 'myregistry3.azurecr.io'\n username: 'myusername'\n passwordSecretRef: 'secret-name'\n }\n ]", + "description": "Optional. Collection of private container registry credentials for containers used by the Container app." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "nullable": true, + "metadata": { + "example": " {\n systemAssigned: true,\n userAssignedResourceIds: [\n '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity'\n ]\n }\n {\n systemAssigned: true\n }\n ", + "description": "Optional. The managed identity definition for this resource." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "nullable": true, + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "containers": { + "type": "array", + "items": { + "$ref": "#/definitions/containerType" + }, + "metadata": { + "description": "Required. List of container definitions for the Container App." + } + }, + "initContainers": { + "type": "array", + "items": { + "$ref": "#/definitions/initContainerType" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of specialized containers that run before app containers." + } + }, + "eventTriggerConfig": { + "$ref": "#/definitions/eventTriggerConfigType", + "nullable": true, + "metadata": { + "description": "Conditional. Configuration of an event driven job. Required if `TriggerType` is `Event`." + } + }, + "scheduleTriggerConfig": { + "$ref": "#/definitions/scheduleTriggerconfigType", + "nullable": true, + "metadata": { + "description": "Conditional. Configuration of a schedule based job. Required if `TriggerType` is `Schedule`." + } + }, + "manualTriggerConfig": { + "$ref": "#/definitions/manualTriggerConfigType", + "nullable": true, + "metadata": { + "description": "Conditional. Configuration of a manually triggered job. Required if `TriggerType` is `Manual`." + } + }, + "replicaRetryLimit": { + "type": "int", + "defaultValue": 0, + "metadata": { + "description": "Optional. The maximum number of times a replica can be retried." + } + }, + "workloadProfileName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the workload profile to use. Leave empty to use a consumption based profile." + } + }, + "secrets": { + "type": "array", + "items": { + "$ref": "#/definitions/secretType" + }, + "nullable": true, + "metadata": { + "example": " [\n {\n name: 'mysecret'\n identity: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity'\n keyVaultUrl: 'https://myvault${environment().suffixes.keyvaultDns}/secrets/mysecret'\n }\n {\n name: 'mysecret'\n identity: 'system'\n keyVaultUrl: 'https://myvault${environment().suffixes.keyvaultDns}/secrets/mysecret'\n }\n {\n // You can do this, but you shouldn't. Use a secret reference instead.\n name: 'mysecret'\n value: 'mysecretvalue'\n }\n {\n name: 'connection-string'\n value: listKeys('/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/myStorageAccount', '2023-04-01').keys[0].value\n }\n ]\n ", + "description": "Optional. The secrets of the Container App." + } + }, + "volumes": { + "type": "array", + "items": { + "$ref": "#/definitions/volumeType" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of volume definitions for the Container App." + } + }, + "replicaTimeout": { + "type": "int", + "defaultValue": 1800, + "metadata": { + "description": "Optional. Maximum number of seconds a replica is allowed to run." + } + }, + "triggerType": { + "type": "string", + "allowedValues": [ + "Event", + "Manual", + "Schedule" + ], + "metadata": { + "description": "Required. Trigger type of the job." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.app-job.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "job": { + "type": "Microsoft.App/jobs", + "apiVersion": "2024-03-01", + "name": "[parameters('name')]", + "tags": "[parameters('tags')]", + "location": "[parameters('location')]", + "identity": "[variables('identity')]", + "properties": { + "environmentId": "[parameters('environmentResourceId')]", + "configuration": { + "triggerType": "[parameters('triggerType')]", + "eventTriggerConfig": "[if(equals(parameters('triggerType'), 'Event'), parameters('eventTriggerConfig'), null())]", + "manualTriggerConfig": "[if(equals(parameters('triggerType'), 'Manual'), parameters('manualTriggerConfig'), null())]", + "scheduleTriggerConfig": "[if(equals(parameters('triggerType'), 'Schedule'), parameters('scheduleTriggerConfig'), null())]", + "replicaRetryLimit": "[parameters('replicaRetryLimit')]", + "replicaTimeout": "[parameters('replicaTimeout')]", + "registries": "[parameters('registries')]", + "secrets": "[parameters('secrets')]" + }, + "template": { + "containers": "[parameters('containers')]", + "initContainers": "[parameters('initContainers')]", + "volumes": "[parameters('volumes')]" + }, + "workloadProfileName": "[parameters('workloadProfileName')]" + } + }, + "job_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.App/jobs/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "job" + ] + }, + "job_roleAssignments": { + "copy": { + "name": "job_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.App/jobs/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.App/jobs', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "job" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "example": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.App/jobs/myJob", + "description": "The resource ID of the Container App Job." + }, + "value": "[resourceId('Microsoft.App/jobs', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "example": "myResourceGroup", + "description": "The name of the resource group the Container App Job was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "example": "myJob", + "description": "The name of the Container App Job." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "example": "Germany West Central", + "description": "The location the resource was deployed into." + }, + "value": "[reference('job', '2024-03-01', 'full').location]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "example": "00000000-0000-0000-0000-000000000000", + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('job', '2024-03-01', 'full'), 'identity'), 'principalId'), '')]" + } + } + } + }, + "dependsOn": [ + "acr", + "appEnvironment", + "taskRun", + "userAssignedIdentity" + ] + }, + "deploymentScriptPrivateDNSZone": { + "condition": "[and(and(and(contains(parameters('computeTypes'), 'azure-container-app'), equals(parameters('selfHostedConfig').selfHostedType, 'azuredevops')), parameters('privateNetworking')), empty(coalesce(tryGet(parameters('networkingConfiguration').computeNetworking, 'deploymentScriptPrivateDnsZoneResourceId'), '')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('stgdsdnszone{0}{1}', parameters('namingPrefix'), uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('privatelink.file.{0}', environment().suffixes.storage)]" + }, + "virtualNetworkLinks": { + "value": [ + { + "virtualNetworkResourceId": "[if(equals(parameters('networkingConfiguration').networkType, 'useExisting'), parameters('networkingConfiguration').virtualNetworkResourceId, reference('newVnet').outputs.resourceId.value)]" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "17411368014038876941" + }, + "name": "Private DNS Zones", + "description": "This module deploys a Private DNS zone.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "aType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv4Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv4 address of this A record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + } + } + }, + "nullable": true + }, + "aaaaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "aaaaRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ipv6Address": { + "type": "string", + "metadata": { + "description": "Required. The IPv6 address of this AAAA record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + } + } + }, + "nullable": true + }, + "cnameType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "cnameRecord": { + "type": "object", + "properties": { + "cname": { + "type": "string", + "metadata": { + "description": "Required. The canonical name of the CNAME record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The CNAME record in the record set." + } + } + } + }, + "nullable": true + }, + "mxType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "mxRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "exchange": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the mail host for this MX record." + } + }, + "preference": { + "type": "int", + "metadata": { + "description": "Required. The preference value for this MX record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + } + } + }, + "nullable": true + }, + "ptrType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "ptrRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ptrdname": { + "type": "string", + "metadata": { + "description": "Required. The PTR target domain name for this PTR record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + } + } + }, + "nullable": true + }, + "soaType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "soaRecord": { + "type": "object", + "properties": { + "email": { + "type": "string", + "metadata": { + "description": "Required. The email contact for this SOA record." + } + }, + "expireTime": { + "type": "int", + "metadata": { + "description": "Required. The expire time for this SOA record." + } + }, + "host": { + "type": "string", + "metadata": { + "description": "Required. The domain name of the authoritative name server for this SOA record." + } + }, + "minimumTtl": { + "type": "int", + "metadata": { + "description": "Required. The minimum value for this SOA record. By convention this is used to determine the negative caching duration." + } + }, + "refreshTime": { + "type": "int", + "metadata": { + "description": "Required. The refresh value for this SOA record." + } + }, + "retryTime": { + "type": "int", + "metadata": { + "description": "Required. The retry time for this SOA record." + } + }, + "serialNumber": { + "type": "int", + "metadata": { + "description": "Required. The serial number for this SOA record." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The SOA record in the record set." + } + } + } + }, + "nullable": true + }, + "srvType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "srvRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "priority": { + "type": "int", + "metadata": { + "description": "Required. The priority value for this SRV record." + } + }, + "weight": { + "type": "int", + "metadata": { + "description": "Required. The weight value for this SRV record." + } + }, + "port": { + "type": "int", + "metadata": { + "description": "Required. The port value for this SRV record." + } + }, + "target": { + "type": "string", + "metadata": { + "description": "Required. The target domain name for this SRV record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + } + } + }, + "nullable": true + }, + "txtType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata of the record." + } + }, + "ttl": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The TTL of the record." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "txtRecords": { + "type": "array", + "items": { + "type": "object", + "properties": { + "value": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The text value of this TXT record." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + } + } + }, + "nullable": true + }, + "virtualNetworkLinkType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "minLength": 1, + "maxLength": 80, + "metadata": { + "description": "Optional. The resource name." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the virtual network to link." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Region where the resource lives." + } + }, + "registrationEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Private DNS zone name." + } + }, + "a": { + "$ref": "#/definitions/aType", + "metadata": { + "description": "Optional. Array of A records." + } + }, + "aaaa": { + "$ref": "#/definitions/aaaaType", + "metadata": { + "description": "Optional. Array of AAAA records." + } + }, + "cname": { + "$ref": "#/definitions/cnameType", + "metadata": { + "description": "Optional. Array of CNAME records." + } + }, + "mx": { + "$ref": "#/definitions/mxType", + "metadata": { + "description": "Optional. Array of MX records." + } + }, + "ptr": { + "$ref": "#/definitions/ptrType", + "metadata": { + "description": "Optional. Array of PTR records." + } + }, + "soa": { + "$ref": "#/definitions/soaType", + "metadata": { + "description": "Optional. Array of SOA records." + } + }, + "srv": { + "$ref": "#/definitions/srvType", + "metadata": { + "description": "Optional. Array of SRV records." + } + }, + "txt": { + "$ref": "#/definitions/txtType", + "metadata": { + "description": "Optional. Array of TXT records." + } + }, + "virtualNetworkLinks": { + "$ref": "#/definitions/virtualNetworkLinkType", + "metadata": { + "description": "Optional. Array of custom objects describing vNet links of the DNS zone. Each object should contain properties 'virtualNetworkResourceId' 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." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.5.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateDnsZone": { + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "privateDnsZone_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_roleAssignments": { + "copy": { + "name": "privateDnsZone_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_A": { + "copy": { + "name": "privateDnsZone_A", + "count": "[length(coalesce(parameters('a'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('a'), createArray())[copyIndex()].name]" + }, + "aRecords": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'aRecords')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('a'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "11343565859504250241" + }, + "name": "Private DNS Zone A record", + "description": "This module deploys a Private DNS Zone A record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the A record." + } + }, + "aRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of A records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "A": { + "type": "Microsoft.Network/privateDnsZones/A", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "aRecords": "[parameters('aRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "A_roleAssignments": { + "copy": { + "name": "A_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "A" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed A record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed A record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed A record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_AAAA": { + "copy": { + "name": "privateDnsZone_AAAA", + "count": "[length(coalesce(parameters('aaaa'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('aaaa'), createArray())[copyIndex()].name]" + }, + "aaaaRecords": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'aaaaRecords')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('aaaa'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "3700934406905797316" + }, + "name": "Private DNS Zone AAAA record", + "description": "This module deploys a Private DNS Zone AAAA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the AAAA record." + } + }, + "aaaaRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of AAAA records in the record set." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "AAAA": { + "type": "Microsoft.Network/privateDnsZones/AAAA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "aaaaRecords": "[parameters('aaaaRecords')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "AAAA_roleAssignments": { + "copy": { + "name": "AAAA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "AAAA" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed AAAA record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed AAAA record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed AAAA record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_CNAME": { + "copy": { + "name": "privateDnsZone_CNAME", + "count": "[length(coalesce(parameters('cname'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('cname'), createArray())[copyIndex()].name]" + }, + "cnameRecord": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'cnameRecord')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'metadata')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('cname'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "12929118683431932277" + }, + "name": "Private DNS Zone CNAME record", + "description": "This module deploys a Private DNS Zone CNAME record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the CNAME record." + } + }, + "cnameRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A CNAME record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "CNAME": { + "type": "Microsoft.Network/privateDnsZones/CNAME", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "cnameRecord": "[parameters('cnameRecord')]", + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "CNAME_roleAssignments": { + "copy": { + "name": "CNAME_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "CNAME" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed CNAME record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed CNAME record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed CNAME record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_MX": { + "copy": { + "name": "privateDnsZone_MX", + "count": "[length(coalesce(parameters('mx'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('mx'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'metadata')]" + }, + "mxRecords": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'mxRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('mx'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "14178508926823177155" + }, + "name": "Private DNS Zone MX record", + "description": "This module deploys a Private DNS Zone MX record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the MX record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "mxRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of MX records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "MX": { + "type": "Microsoft.Network/privateDnsZones/MX", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "mxRecords": "[parameters('mxRecords')]", + "ttl": "[parameters('ttl')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "MX_roleAssignments": { + "copy": { + "name": "MX_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "MX" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed MX record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed MX record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed MX record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_PTR": { + "copy": { + "name": "privateDnsZone_PTR", + "count": "[length(coalesce(parameters('ptr'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('ptr'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'metadata')]" + }, + "ptrRecords": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ptrRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('ptr'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "8082128465097964607" + }, + "name": "Private DNS Zone PTR record", + "description": "This module deploys a Private DNS Zone PTR record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the PTR record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ptrRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of PTR records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "PTR": { + "type": "Microsoft.Network/privateDnsZones/PTR", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "ptrRecords": "[parameters('ptrRecords')]", + "ttl": "[parameters('ttl')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "PTR_roleAssignments": { + "copy": { + "name": "PTR_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "PTR" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed PTR record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed PTR record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed PTR record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_SOA": { + "copy": { + "name": "privateDnsZone_SOA", + "count": "[length(coalesce(parameters('soa'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('soa'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'metadata')]" + }, + "soaRecord": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'soaRecord')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('soa'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "6755707328778392735" + }, + "name": "Private DNS Zone SOA record", + "description": "This module deploys a Private DNS Zone SOA record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SOA record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "soaRecord": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. A SOA record." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SOA": { + "type": "Microsoft.Network/privateDnsZones/SOA", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "soaRecord": "[parameters('soaRecord')]", + "ttl": "[parameters('ttl')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "SOA_roleAssignments": { + "copy": { + "name": "SOA_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "SOA" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SOA record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SOA record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SOA record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_SRV": { + "copy": { + "name": "privateDnsZone_SRV", + "count": "[length(coalesce(parameters('srv'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('srv'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'metadata')]" + }, + "srvRecords": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'srvRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('srv'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "662436323695062210" + }, + "name": "Private DNS Zone SRV record", + "description": "This module deploys a Private DNS Zone SRV record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the SRV record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "srvRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of SRV records in the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "SRV": { + "type": "Microsoft.Network/privateDnsZones/SRV", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "srvRecords": "[parameters('srvRecords')]", + "ttl": "[parameters('ttl')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "SRV_roleAssignments": { + "copy": { + "name": "SRV_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "SRV" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed SRV record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed SRV record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed SRV record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_TXT": { + "copy": { + "name": "privateDnsZone_TXT", + "count": "[length(coalesce(parameters('txt'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('txt'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'metadata')]" + }, + "txtRecords": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'txtRecords')]" + }, + "ttl": { + "value": "[coalesce(tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'ttl'), 3600)]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('txt'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "356727884506799436" + }, + "name": "Private DNS Zone TXT record", + "description": "This module deploys a Private DNS Zone TXT record.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the TXT record." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The metadata attached to the record set." + } + }, + "ttl": { + "type": "int", + "defaultValue": 3600, + "metadata": { + "description": "Optional. The TTL (time-to-live) of the records in the record set." + } + }, + "txtRecords": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The list of TXT records in the record set." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "TXT": { + "type": "Microsoft.Network/privateDnsZones/TXT", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]", + "ttl": "[parameters('ttl')]", + "txtRecords": "[parameters('txtRecords')]" + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "TXT_roleAssignments": { + "copy": { + "name": "TXT_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "TXT" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed TXT record." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed TXT record." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed TXT record." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + }, + "privateDnsZone_virtualNetworkLinks": { + "copy": { + "name": "privateDnsZone_virtualNetworkLinks", + "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateDnsZone-VirtualNetworkLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'name'), format('{0}-vnetlink', last(split(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId, '/'))))]" + }, + "virtualNetworkResourceId": { + "value": "[coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()].virtualNetworkResourceId]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'location'), 'global')]" + }, + "registrationEnabled": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'registrationEnabled'), false())]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "1713449351614683457" + }, + "name": "Private DNS Zone Virtual Network Link", + "description": "This module deploys a Private DNS Zone Virtual Network Link.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateDnsZoneName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Private DNS zone. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "[format('{0}-vnetlink', last(split(parameters('virtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The name of the virtual network link." + } + }, + "location": { + "type": "string", + "defaultValue": "global", + "metadata": { + "description": "Optional. The location of the PrivateDNSZone. Should be global." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "registrationEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Is auto-registration of virtual machine records in the virtual network in the Private DNS zone enabled?." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. Link to another virtual network resource ID." + } + } + }, + "resources": { + "privateDnsZone": { + "existing": true, + "type": "Microsoft.Network/privateDnsZones", + "apiVersion": "2020-06-01", + "name": "[parameters('privateDnsZoneName')]" + }, + "virtualNetworkLink": { + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "registrationEnabled": "[parameters('registrationEnabled')]", + "virtualNetwork": { + "id": "[parameters('virtualNetworkResourceId')]" + } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed virtual network link." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed virtual network link." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones/virtualNetworkLinks', parameters('privateDnsZoneName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed virtual network link." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetworkLink', '2020-06-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "privateDnsZone" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private DNS zone was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private DNS zone." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private DNS zone." + }, + "value": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateDnsZone', '2020-06-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "newVnet" + ] + }, + "deploymentScriptStg": { + "condition": "[and(and(contains(parameters('computeTypes'), 'azure-container-app'), equals(parameters('selfHostedConfig').selfHostedType, 'azuredevops')), parameters('privateNetworking'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('placeholderAgentDeploymentScript-{0}', uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('stgds{0}', uniqueString(resourceGroup().id, coalesce(tryGet(parameters('selfHostedConfig'), 'placeHolderAgentName'), 'placeHolderAgent'), parameters('location'), parameters('selfHostedConfig').agentsPoolName))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "allowBlobPublicAccess": { + "value": false + }, + "allowSharedKeyAccess": { + "value": true + }, + "publicNetworkAccess": { + "value": "Disabled" + }, + "roleAssignments": { + "value": [ + { + "principalId": "[reference('userAssignedIdentity').outputs.principalId.value]", + "roleDefinitionIdOrName": "69566ab7-960f-475b-8e7c-b3118f30c6bd", + "principalType": "ServicePrincipal" + } + ] + }, + "privateEndpoints": { + "value": [ + { + "service": "file", + "subnetResourceId": "[if(equals(parameters('networkingConfiguration').networkType, 'useExisting'), format('{0}/subnets/{1}', parameters('networkingConfiguration').virtualNetworkResourceId, parameters('networkingConfiguration').computeNetworking.containerAppDeploymentScriptSubnetName), filter(reference('newVnet').outputs.subnetResourceIds.value, lambda('subnetId', contains(lambdaVariables('subnetId'), coalesce(tryGet(parameters('networkingConfiguration'), 'containerAppDeploymentScriptSubnetName'), 'app-deployment-script-subnet'))))[0])]", + "privateDnsZoneGroupName": "stgPrivateDNSZoneGroup", + "privateDnsZoneResourceIds": [ + "[coalesce(tryGet(parameters('networkingConfiguration').computeNetworking, 'deploymentScriptPrivateDnsZoneResourceId'), reference('deploymentScriptPrivateDNSZone').outputs.resourceId.value)]" + ] + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "1565933701830377410" + }, + "name": "Storage Accounts", + "description": "This module deploys a Storage Account.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "networkAclsType": { + "type": "object", + "properties": { + "resourceAccessRules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "tenantId": { + "type": "string", + "metadata": { + "description": "Required. The ID of the tenant in which the resource resides in." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the target service. Can also contain a wildcard, if multiple services e.g. in a resource group should be included." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Sets the resource access rules. Array entries must consist of \"tenantId\" and \"resourceId\" fields only." + } + }, + "bypass": { + "type": "string", + "allowedValues": [ + "AzureServices", + "AzureServices, Logging", + "AzureServices, Logging, Metrics", + "AzureServices, Metrics", + "Logging", + "Logging, Metrics", + "Metrics", + "None" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies whether traffic is bypassed for Logging/Metrics/AzureServices. Possible values are any combination of Logging,Metrics,AzureServices (For example, \"Logging, Metrics\"), or None to bypass none of those traffics." + } + }, + "virtualNetworkRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Sets the virtual network rules." + } + }, + "ipRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Sets the IP ACL rules." + } + }, + "defaultAction": { + "type": "string", + "allowedValues": [ + "Allow", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the default action of allow or deny when no other rules match." + } + } + } + }, + "privateEndpointType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "metadata": { + "description": "Required. The subresource to deploy the private endpoint for. For example \"blob\", \"table\", \"queue\" or \"file\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint ip address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private ip addresses of the private endpoint." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private ip address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. If used must also be specified in `managedIdentities.userAssignedResourceIds`. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true + }, + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." + } + }, + "accessKey1": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The accessKey1 secret name to create." + } + }, + "connectionString1": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The connectionString1 secret name to create." + } + }, + "accessKey2": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The accessKey2 secret name to create." + } + }, + "connectionString2": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The connectionString2 secret name to create." + } + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/secretSetType", + "metadata": { + "description": "An exported secret's references." + } + } + }, + "secretSetType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "modules/keyVaultExport.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Required. Name of the Storage Account. Must be lower-case." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "kind": { + "type": "string", + "defaultValue": "StorageV2", + "allowedValues": [ + "Storage", + "StorageV2", + "BlobStorage", + "FileStorage", + "BlockBlobStorage" + ], + "metadata": { + "description": "Optional. Type of Storage Account to create." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Standard_GRS", + "allowedValues": [ + "Standard_LRS", + "Standard_GRS", + "Standard_RAGRS", + "Standard_ZRS", + "Premium_LRS", + "Premium_ZRS", + "Standard_GZRS", + "Standard_RAGZRS" + ], + "metadata": { + "description": "Optional. Storage Account Sku Name." + } + }, + "accessTier": { + "type": "string", + "defaultValue": "Hot", + "allowedValues": [ + "Premium", + "Hot", + "Cool" + ], + "metadata": { + "description": "Conditional. Required if the Storage Account kind is set to BlobStorage. The access tier is used for billing. The \"Premium\" access tier is the default value for premium block blobs storage account type and it cannot be changed for the premium block blobs storage account type." + } + }, + "largeFileSharesState": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Allow large file shares if sets to 'Enabled'. It cannot be disabled once it is enabled. Only supported on locally redundant and zone redundant file shares. It cannot be set on FileStorage storage accounts (storage accounts for premium file shares)." + } + }, + "azureFilesIdentityBasedAuthentication": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Provides the identity based authentication settings for Azure Files." + } + }, + "defaultToOAuthAuthentication": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. A boolean flag which indicates whether the default authentication is OAuth or not." + } + }, + "allowSharedKeyAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether the storage account permits requests to be authorized with the account access key via Shared Key. If false, then all requests, including shared access signatures, must be authorized with Azure Active Directory (Azure AD). The default value is null, which is equivalent to true." + } + }, + "privateEndpoints": { + "$ref": "#/definitions/privateEndpointType", + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "managementPolicyRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The Storage Account ManagementPolicies Rules." + } + }, + "networkAcls": { + "$ref": "#/definitions/networkAclsType", + "nullable": true, + "metadata": { + "description": "Optional. Networks ACLs, this value contains IPs to whitelist and/or Subnet information. If in use, bypass needs to be supplied. For security reasons, it is recommended to set the DefaultAction Deny." + } + }, + "requireInfrastructureEncryption": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. A Boolean indicating whether or not the service applies a secondary layer of encryption with platform managed keys for data at rest. For security reasons, it is recommended to set it to true." + } + }, + "allowCrossTenantReplication": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Allow or disallow cross AAD tenant object replication." + } + }, + "customDomainName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Sets the custom domain name assigned to the storage account. Name is the CNAME source." + } + }, + "customDomainUseSubDomainName": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether indirect CName validation is enabled. This should only be set on updates." + } + }, + "dnsEndpointType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "AzureDnsZone", + "Standard" + ], + "metadata": { + "description": "Optional. Allows you to specify the type of endpoint. Set this to AzureDNSZone to create a large number of accounts in a single subscription, which creates accounts in an Azure DNS Zone and the endpoint URL will have an alphanumeric DNS Zone identifier." + } + }, + "blobServices": { + "type": "object", + "defaultValue": "[if(not(equals(parameters('kind'), 'FileStorage')), createObject('containerDeleteRetentionPolicyEnabled', true(), 'containerDeleteRetentionPolicyDays', 7, 'deleteRetentionPolicyEnabled', true(), 'deleteRetentionPolicyDays', 6), createObject())]", + "metadata": { + "description": "Optional. Blob service and containers to deploy." + } + }, + "fileServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. File service and shares to deploy." + } + }, + "queueServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Queue service and queues to create." + } + }, + "tableServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Table service and tables to create." + } + }, + "allowBlobPublicAccess": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether public access is enabled for all blobs or containers in the storage account. For security reasons, it is recommended to set it to false." + } + }, + "minimumTlsVersion": { + "type": "string", + "defaultValue": "TLS1_2", + "allowedValues": [ + "TLS1_0", + "TLS1_1", + "TLS1_2" + ], + "metadata": { + "description": "Optional. Set the minimum TLS version on request to storage." + } + }, + "enableHierarchicalNamespace": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Conditional. If true, enables Hierarchical Namespace for the storage account. Required if enableSftp or enableNfsV3 is set to true." + } + }, + "enableSftp": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If true, enables Secure File Transfer Protocol for the storage account. Requires enableHierarchicalNamespace to be true." + } + }, + "localUsers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Local users to deploy for SFTP authentication." + } + }, + "isLocalUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables local users feature, if set to true." + } + }, + "enableNfsV3": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If true, enables NFS 3.0 support for the storage account. Requires enableHierarchicalNamespace to be true." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "allowedCopyScope": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "AAD", + "PrivateLink" + ], + "metadata": { + "description": "Optional. Restrict copy to and from Storage Accounts within an AAD tenant or with Private Links to the same VNet." + } + }, + "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 and networkAcls are not set." + } + }, + "supportsHttpsTrafficOnly": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allows HTTPS traffic only to storage service if sets to true." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "sasExpirationPeriod": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The SAS expiration period. DD.HH:MM:SS." + } + }, + "keyType": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Account", + "Service" + ], + "metadata": { + "description": "Optional. The keyType to use with Queue & Table services." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "supportsBlobService": "[or(or(or(equals(parameters('kind'), 'BlockBlobStorage'), equals(parameters('kind'), 'BlobStorage')), equals(parameters('kind'), 'StorageV2')), equals(parameters('kind'), 'Storage'))]", + "supportsFileService": "[or(or(equals(parameters('kind'), 'FileStorage'), equals(parameters('kind'), 'StorageV2')), equals(parameters('kind'), 'Storage'))]", + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "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')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Blob Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "Storage Blob Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", + "Storage Blob Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')]", + "Storage Blob Delegator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a')]", + "Storage File Data Privileged Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69566ab7-960f-475b-8e7c-b3118f30c6bd')]", + "Storage File Data Privileged Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b8eda974-7b85-4f76-af95-65846b26df6d')]", + "Storage File Data SMB Share Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb')]", + "Storage File Data SMB Share Elevated Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7')]", + "Storage File Data SMB Share Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314')]", + "Storage Queue Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]", + "Storage Queue Data Message Processor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]", + "Storage Queue Data Message Sender": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a')]", + "Storage Queue Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925')]", + "Storage Table Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]", + "Storage Table Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76199698-9eea-4c19-bc75-cec21354c6b6')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", + "dependsOn": [ + "cMKKeyVault" + ] + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.storage-storageaccount.{0}.{1}', replace('0.13.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + }, + "storageAccount": { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "kind": "[parameters('kind')]", + "sku": { + "name": "[parameters('skuName')]" + }, + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "properties": { + "allowSharedKeyAccess": "[parameters('allowSharedKeyAccess')]", + "defaultToOAuthAuthentication": "[parameters('defaultToOAuthAuthentication')]", + "allowCrossTenantReplication": "[parameters('allowCrossTenantReplication')]", + "allowedCopyScope": "[if(not(empty(parameters('allowedCopyScope'))), parameters('allowedCopyScope'), null())]", + "customDomain": { + "name": "[parameters('customDomainName')]", + "useSubDomainName": "[parameters('customDomainUseSubDomainName')]" + }, + "dnsEndpointType": "[if(not(empty(parameters('dnsEndpointType'))), parameters('dnsEndpointType'), null())]", + "isLocalUserEnabled": "[parameters('isLocalUserEnabled')]", + "encryption": "[union(createObject('keySource', if(not(empty(parameters('customerManagedKey'))), 'Microsoft.Keyvault', 'Microsoft.Storage'), 'services', createObject('blob', if(variables('supportsBlobService'), createObject('enabled', true()), null()), 'file', if(variables('supportsFileService'), createObject('enabled', true()), null()), 'table', createObject('enabled', true(), 'keyType', parameters('keyType')), 'queue', createObject('enabled', true(), 'keyType', parameters('keyType'))), 'keyvaultproperties', if(not(empty(parameters('customerManagedKey'))), createObject('keyname', parameters('customerManagedKey').keyName, 'keyvaulturi', reference('cMKKeyVault').vaultUri, 'keyversion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/')))), null()), 'identity', createObject('userAssignedIdentity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))), null()))), if(parameters('requireInfrastructureEncryption'), createObject('requireInfrastructureEncryption', if(not(equals(parameters('kind'), 'Storage')), parameters('requireInfrastructureEncryption'), null())), createObject()))]", + "accessTier": "[if(and(not(equals(parameters('kind'), 'Storage')), not(equals(parameters('kind'), 'BlockBlobStorage'))), parameters('accessTier'), null())]", + "sasPolicy": "[if(not(empty(parameters('sasExpirationPeriod'))), createObject('expirationAction', 'Log', 'sasExpirationPeriod', parameters('sasExpirationPeriod')), null())]", + "supportsHttpsTrafficOnly": "[parameters('supportsHttpsTrafficOnly')]", + "isHnsEnabled": "[if(parameters('enableHierarchicalNamespace'), parameters('enableHierarchicalNamespace'), null())]", + "isSftpEnabled": "[parameters('enableSftp')]", + "isNfsV3Enabled": "[if(parameters('enableNfsV3'), parameters('enableNfsV3'), '')]", + "largeFileSharesState": "[if(or(equals(parameters('skuName'), 'Standard_LRS'), equals(parameters('skuName'), 'Standard_ZRS')), parameters('largeFileSharesState'), null())]", + "minimumTlsVersion": "[parameters('minimumTlsVersion')]", + "networkAcls": "[if(not(empty(parameters('networkAcls'))), union(createObject('resourceAccessRules', tryGet(parameters('networkAcls'), 'resourceAccessRules'), 'defaultAction', coalesce(tryGet(parameters('networkAcls'), 'defaultAction'), 'Deny'), 'virtualNetworkRules', tryGet(parameters('networkAcls'), 'virtualNetworkRules'), 'ipRules', tryGet(parameters('networkAcls'), 'ipRules')), if(contains(parameters('networkAcls'), 'bypass'), createObject('bypass', tryGet(parameters('networkAcls'), 'bypass')), createObject())), createObject('bypass', 'AzureServices', 'defaultAction', 'Deny'))]", + "allowBlobPublicAccess": "[parameters('allowBlobPublicAccess')]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkAcls'))), 'Disabled', null()))]", + "azureFilesIdentityBasedAuthentication": "[if(not(empty(parameters('azureFilesIdentityBasedAuthentication'))), parameters('azureFilesIdentityBasedAuthentication'), null())]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKUserAssignedIdentity" + ] + }, + "storageAccount_diagnosticSettings": { + "copy": { + "name": "storageAccount_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_roleAssignments": { + "copy": { + "name": "storageAccount_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_privateEndpoints": { + "copy": { + "name": "storageAccount_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-storageAccount-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroupName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" + }, + "privateDnsZoneResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "13720311665093076615" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "ipConfigurationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "manualPrivateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "$ref": "#/definitions/ipConfigurationsType", + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + } + }, + "privateLinkServiceConnections": { + "$ref": "#/definitions/privateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.6.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneResourceIds')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" + }, + "privateDNSResourceIds": { + "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "15263454436186512874" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_managementPolicies": { + "condition": "[not(empty(coalesce(parameters('managementPolicyRules'), createArray())))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-ManagementPolicies', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "rules": { + "value": "[coalesce(parameters('managementPolicyRules'), createArray())]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "11289787713365096902" + }, + "name": "Storage Account Management Policies", + "description": "This module deploys a Storage Account Management Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "rules": { + "type": "array", + "metadata": { + "description": "Required. The Storage Account ManagementPolicies Rules." + } + } + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts/managementPolicies", + "apiVersion": "2023-01-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "properties": { + "policy": { + "rules": "[parameters('rules')]" + } + } + } + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed management policy." + }, + "value": "default" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed management policy." + }, + "value": "default" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed management policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount", + "storageAccount_blobServices" + ] + }, + "storageAccount_localUsers": { + "copy": { + "name": "storageAccount_localUsers", + "count": "[length(parameters('localUsers'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-LocalUsers-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('localUsers')[copyIndex()].name]" + }, + "hasSshKey": { + "value": "[parameters('localUsers')[copyIndex()].hasSshKey]" + }, + "hasSshPassword": { + "value": "[parameters('localUsers')[copyIndex()].hasSshPassword]" + }, + "permissionScopes": { + "value": "[parameters('localUsers')[copyIndex()].permissionScopes]" + }, + "hasSharedKey": { + "value": "[tryGet(parameters('localUsers')[copyIndex()], 'hasSharedKey')]" + }, + "homeDirectory": { + "value": "[tryGet(parameters('localUsers')[copyIndex()], 'homeDirectory')]" + }, + "sshAuthorizedKeys": { + "value": "[tryGet(parameters('localUsers')[copyIndex()], 'sshAuthorizedKeys')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "18130658711251621530" + }, + "name": "Storage Account Local Users", + "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "sshAuthorizedKeysType": { + "type": "secureObject", + "properties": { + "secureList": { + "type": "array", + "items": { + "type": "object", + "properties": { + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description used to store the function/usage of the key." + } + }, + "key": { + "type": "string", + "metadata": { + "description": "Required. SSH public key base64 encoded. The format should be: '{keyType} {keyData}', e.g. ssh-rsa AAAABBBB." + } + } + } + }, + "metadata": { + "description": "Optional. The list of SSH authorized keys." + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the local user used for SFTP Authentication." + } + }, + "hasSharedKey": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether shared key exists. Set it to false to remove existing shared key." + } + }, + "hasSshKey": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether SSH key exists. Set it to false to remove existing SSH key." + } + }, + "hasSshPassword": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether SSH password exists. Set it to false to remove existing SSH password." + } + }, + "homeDirectory": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The local user home directory." + } + }, + "permissionScopes": { + "type": "array", + "metadata": { + "description": "Required. The permission scopes of the local user." + } + }, + "sshAuthorizedKeys": { + "$ref": "#/definitions/sshAuthorizedKeysType", + "metadata": { + "description": "Optional. The local user SSH authorized keys for SFTP." + } + } + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "localUsers": { + "type": "Microsoft.Storage/storageAccounts/localUsers", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]", + "properties": { + "hasSharedKey": "[parameters('hasSharedKey')]", + "hasSshKey": "[parameters('hasSshKey')]", + "hasSshPassword": "[parameters('hasSshPassword')]", + "homeDirectory": "[parameters('homeDirectory')]", + "permissionScopes": "[parameters('permissionScopes')]", + "sshAuthorizedKeys": "[tryGet(parameters('sshAuthorizedKeys'), 'secureList')]" + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed local user." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed local user." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed local user." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/localUsers', parameters('storageAccountName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_blobServices": { + "condition": "[not(empty(parameters('blobServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-BlobServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "containers": { + "value": "[tryGet(parameters('blobServices'), 'containers')]" + }, + "automaticSnapshotPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'automaticSnapshotPolicyEnabled')]" + }, + "changeFeedEnabled": { + "value": "[tryGet(parameters('blobServices'), 'changeFeedEnabled')]" + }, + "changeFeedRetentionInDays": { + "value": "[tryGet(parameters('blobServices'), 'changeFeedRetentionInDays')]" + }, + "containerDeleteRetentionPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyEnabled')]" + }, + "containerDeleteRetentionPolicyDays": { + "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyDays')]" + }, + "containerDeleteRetentionPolicyAllowPermanentDelete": { + "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyAllowPermanentDelete')]" + }, + "corsRules": { + "value": "[tryGet(parameters('blobServices'), 'corsRules')]" + }, + "defaultServiceVersion": { + "value": "[tryGet(parameters('blobServices'), 'defaultServiceVersion')]" + }, + "deleteRetentionPolicyAllowPermanentDelete": { + "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyAllowPermanentDelete')]" + }, + "deleteRetentionPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyEnabled')]" + }, + "deleteRetentionPolicyDays": { + "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyDays')]" + }, + "isVersioningEnabled": { + "value": "[tryGet(parameters('blobServices'), 'isVersioningEnabled')]" + }, + "lastAccessTimeTrackingPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'lastAccessTimeTrackingPolicyEnabled')]" + }, + "restorePolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'restorePolicyEnabled')]" + }, + "restorePolicyDays": { + "value": "[tryGet(parameters('blobServices'), 'restorePolicyDays')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('blobServices'), 'diagnosticSettings')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "6152214765227449988" + }, + "name": "Storage Account blob Services", + "description": "This module deploys a Storage Account Blob Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "automaticSnapshotPolicyEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Automatic Snapshot is enabled if set to true." + } + }, + "changeFeedEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The blob service properties for change feed events. Indicates whether change feed event logging is enabled for the Blob service." + } + }, + "changeFeedRetentionInDays": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 146000, + "metadata": { + "description": "Optional. Indicates whether change feed event logging is enabled for the Blob service. Indicates the duration of changeFeed retention in days. If left blank, it indicates an infinite retention of the change feed." + } + }, + "containerDeleteRetentionPolicyEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The blob service properties for container soft delete. Indicates whether DeleteRetentionPolicy is enabled." + } + }, + "containerDeleteRetentionPolicyDays": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 365, + "metadata": { + "description": "Optional. Indicates the number of days that the deleted item should be retained." + } + }, + "containerDeleteRetentionPolicyAllowPermanentDelete": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. This property when set to true allows deletion of the soft deleted blob versions and snapshots. This property cannot be used with blob restore policy. This property only applies to blob service and does not apply to containers or file share." + } + }, + "corsRules": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies CORS rules for the Blob service. You can include up to five CorsRule elements in the request. If no CorsRule elements are included in the request body, all CORS rules will be deleted, and CORS will be disabled for the Blob service." + } + }, + "defaultServiceVersion": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Indicates the default version to use for requests to the Blob service if an incoming request's version is not specified. Possible values include version 2008-10-27 and all more recent versions." + } + }, + "deleteRetentionPolicyEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The blob service properties for blob soft delete." + } + }, + "deleteRetentionPolicyDays": { + "type": "int", + "defaultValue": 7, + "minValue": 1, + "maxValue": 365, + "metadata": { + "description": "Optional. Indicates the number of days that the deleted blob should be retained." + } + }, + "deleteRetentionPolicyAllowPermanentDelete": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. This property when set to true allows deletion of the soft deleted blob versions and snapshots. This property cannot be used with blob restore policy. This property only applies to blob service and does not apply to containers or file share." + } + }, + "isVersioningEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Use versioning to automatically maintain previous versions of your blobs." + } + }, + "lastAccessTimeTrackingPolicyEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The blob service property to configure last access time based tracking policy. When set to true last access time based tracking is enabled." + } + }, + "restorePolicyEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The blob service properties for blob restore policy. If point-in-time restore is enabled, then versioning, change feed, and blob soft delete must also be enabled." + } + }, + "restorePolicyDays": { + "type": "int", + "defaultValue": 6, + "minValue": 1, + "metadata": { + "description": "Optional. How long this blob can be restored. It should be less than DeleteRetentionPolicy days." + } + }, + "containers": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Blob containers to create." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "name": "default" + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[parameters('storageAccountName')]" + }, + "blobServices": { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "properties": { + "automaticSnapshotPolicyEnabled": "[parameters('automaticSnapshotPolicyEnabled')]", + "changeFeed": "[if(parameters('changeFeedEnabled'), createObject('enabled', true(), 'retentionInDays', parameters('changeFeedRetentionInDays')), null())]", + "containerDeleteRetentionPolicy": { + "enabled": "[parameters('containerDeleteRetentionPolicyEnabled')]", + "days": "[parameters('containerDeleteRetentionPolicyDays')]", + "allowPermanentDelete": "[if(equals(parameters('containerDeleteRetentionPolicyEnabled'), true()), parameters('containerDeleteRetentionPolicyAllowPermanentDelete'), null())]" + }, + "cors": { + "corsRules": "[parameters('corsRules')]" + }, + "defaultServiceVersion": "[if(not(empty(parameters('defaultServiceVersion'))), parameters('defaultServiceVersion'), null())]", + "deleteRetentionPolicy": { + "enabled": "[parameters('deleteRetentionPolicyEnabled')]", + "days": "[parameters('deleteRetentionPolicyDays')]", + "allowPermanentDelete": "[if(and(parameters('deleteRetentionPolicyEnabled'), parameters('deleteRetentionPolicyAllowPermanentDelete')), true(), null())]" + }, + "isVersioningEnabled": "[parameters('isVersioningEnabled')]", + "lastAccessTimeTrackingPolicy": "[if(not(equals(reference('storageAccount', '2022-09-01', 'full').kind, 'Storage')), createObject('enable', parameters('lastAccessTimeTrackingPolicyEnabled'), 'name', if(equals(parameters('lastAccessTimeTrackingPolicyEnabled'), true()), 'AccessTimeTracking', null()), 'trackingGranularityInDays', if(equals(parameters('lastAccessTimeTrackingPolicyEnabled'), true()), 1, null())), null())]", + "restorePolicy": "[if(parameters('restorePolicyEnabled'), createObject('enabled', true(), 'days', parameters('restorePolicyDays')), null())]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "blobServices_diagnosticSettings": { + "copy": { + "name": "blobServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}', parameters('storageAccountName'), variables('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "blobServices" + ] + }, + "blobServices_container": { + "copy": { + "name": "blobServices_container", + "count": "[length(coalesce(parameters('containers'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Container-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "name": { + "value": "[coalesce(parameters('containers'), createArray())[copyIndex()].name]" + }, + "defaultEncryptionScope": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'defaultEncryptionScope')]" + }, + "denyEncryptionScopeOverride": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'denyEncryptionScopeOverride')]" + }, + "enableNfsV3AllSquash": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'enableNfsV3AllSquash')]" + }, + "enableNfsV3RootSquash": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'enableNfsV3RootSquash')]" + }, + "immutableStorageWithVersioningEnabled": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'immutableStorageWithVersioningEnabled')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'metadata')]" + }, + "publicAccess": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'publicAccess')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "immutabilityPolicyProperties": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'immutabilityPolicyProperties')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "2376441074312126168" + }, + "name": "Storage Account Blob Containers", + "description": "This module deploys a Storage Account Blob Container.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the storage container to deploy." + } + }, + "defaultEncryptionScope": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Default the container to use specified encryption scope for all writes." + } + }, + "denyEncryptionScopeOverride": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Block override of encryption scope from the container default." + } + }, + "enableNfsV3AllSquash": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable NFSv3 all squash on blob container." + } + }, + "enableNfsV3RootSquash": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable NFSv3 root squash on blob container." + } + }, + "immutableStorageWithVersioningEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. This is an immutable property, when set to true it enables object level immutability at the container level. The property is immutable and can only be set to true at the container creation time. Existing containers must undergo a migration process." + } + }, + "immutabilityPolicyName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. Name of the immutable policy." + } + }, + "immutabilityPolicyProperties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Configure immutability policy." + } + }, + "metadata": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. A name-value pair to associate with the container as metadata." + } + }, + "publicAccess": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "Container", + "Blob", + "None" + ], + "metadata": { + "description": "Optional. Specifies whether data in the container may be accessed publicly and the level of access." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "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')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Blob Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "Storage Blob Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", + "Storage Blob Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')]", + "Storage Blob Delegator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::blobServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[parameters('storageAccountName')]" + }, + "container": { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "properties": { + "defaultEncryptionScope": "[if(not(empty(parameters('defaultEncryptionScope'))), parameters('defaultEncryptionScope'), null())]", + "denyEncryptionScopeOverride": "[if(equals(parameters('denyEncryptionScopeOverride'), true()), parameters('denyEncryptionScopeOverride'), null())]", + "enableNfsV3AllSquash": "[if(equals(parameters('enableNfsV3AllSquash'), true()), parameters('enableNfsV3AllSquash'), null())]", + "enableNfsV3RootSquash": "[if(equals(parameters('enableNfsV3RootSquash'), true()), parameters('enableNfsV3RootSquash'), null())]", + "immutableStorageWithVersioning": "[if(equals(parameters('immutableStorageWithVersioningEnabled'), true()), createObject('enabled', parameters('immutableStorageWithVersioningEnabled')), null())]", + "metadata": "[parameters('metadata')]", + "publicAccess": "[parameters('publicAccess')]" + }, + "dependsOn": [ + "storageAccount::blobServices" + ] + }, + "container_roleAssignments": { + "copy": { + "name": "container_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}/containers/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "container" + ] + }, + "immutabilityPolicy": { + "condition": "[not(empty(coalesce(parameters('immutabilityPolicyProperties'), createObject())))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[parameters('immutabilityPolicyName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "containerName": { + "value": "[parameters('name')]" + }, + "immutabilityPeriodSinceCreationInDays": { + "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'immutabilityPeriodSinceCreationInDays')]" + }, + "allowProtectedAppendWrites": { + "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'allowProtectedAppendWrites')]" + }, + "allowProtectedAppendWritesAll": { + "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'allowProtectedAppendWritesAll')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "7245741358008626948" + }, + "name": "Storage Account Blob Container Immutability Policies", + "description": "This module deploys a Storage Account Blob Container Immutability Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "containerName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent container to apply the policy to. Required if the template is used in a standalone deployment." + } + }, + "immutabilityPeriodSinceCreationInDays": { + "type": "int", + "defaultValue": 365, + "metadata": { + "description": "Optional. The immutability period for the blobs in the container since the policy creation, in days." + } + }, + "allowProtectedAppendWrites": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to an append blob while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API." + } + }, + "allowProtectedAppendWritesAll": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to both \"Append and Block Blobs\" while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API. The \"allowProtectedAppendWrites\" and \"allowProtectedAppendWritesAll\" properties are mutually exclusive." + } + } + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}/{2}/{3}', parameters('storageAccountName'), 'default', parameters('containerName'), 'default')]", + "properties": { + "immutabilityPeriodSinceCreationInDays": "[parameters('immutabilityPeriodSinceCreationInDays')]", + "allowProtectedAppendWrites": "[parameters('allowProtectedAppendWrites')]", + "allowProtectedAppendWritesAll": "[parameters('allowProtectedAppendWritesAll')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed immutability policy." + }, + "value": "default" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed immutability policy." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies', parameters('storageAccountName'), 'default', parameters('containerName'), 'default')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed immutability policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "container", + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed container." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed container." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed container." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed blob service." + }, + "value": "[variables('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed blob service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccountName'), variables('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the deployed blob service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_fileServices": { + "condition": "[not(empty(parameters('fileServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-FileServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('fileServices'), 'diagnosticSettings')]" + }, + "protocolSettings": { + "value": "[tryGet(parameters('fileServices'), 'protocolSettings')]" + }, + "shareDeleteRetentionPolicy": { + "value": "[tryGet(parameters('fileServices'), 'shareDeleteRetentionPolicy')]" + }, + "shares": { + "value": "[tryGet(parameters('fileServices'), 'shares')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "11112322068652007329" + }, + "name": "Storage Account File Share Services", + "description": "This module deploys a Storage Account File Share Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the file service." + } + }, + "protocolSettings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Protocol settings for file service." + } + }, + "shareDeleteRetentionPolicy": { + "type": "object", + "defaultValue": { + "enabled": true, + "days": 7 + }, + "metadata": { + "description": "Optional. The service properties for soft delete." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "shares": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. File shares to create." + } + } + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "fileServices": { + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]", + "properties": { + "protocolSettings": "[parameters('protocolSettings')]", + "shareDeleteRetentionPolicy": "[parameters('shareDeleteRetentionPolicy')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "fileServices_diagnosticSettings": { + "copy": { + "name": "fileServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/fileServices/{1}', parameters('storageAccountName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "fileServices" + ] + }, + "fileServices_shares": { + "copy": { + "name": "fileServices_shares", + "count": "[length(coalesce(parameters('shares'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-shares-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "fileServicesName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('shares'), createArray())[copyIndex()].name]" + }, + "accessTier": { + "value": "[coalesce(tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'accessTier'), if(equals(reference('storageAccount', '2023-04-01', 'full').kind, 'FileStorage'), 'Premium', 'TransactionOptimized'))]" + }, + "enabledProtocols": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'enabledProtocols')]" + }, + "rootSquash": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'rootSquash')]" + }, + "shareQuota": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'shareQuota')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "7774942141784896900" + }, + "name": "Storage Account File Shares", + "description": "This module deploys a Storage Account File Share.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "fileServicesName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Conditional. The name of the parent file service. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the file share to create." + } + }, + "accessTier": { + "type": "string", + "defaultValue": "TransactionOptimized", + "allowedValues": [ + "Premium", + "Hot", + "Cool", + "TransactionOptimized" + ], + "metadata": { + "description": "Conditional. Access tier for specific share. Required if the Storage Account kind is set to FileStorage (should be set to \"Premium\"). GpV2 account can choose between TransactionOptimized (default), Hot, and Cool." + } + }, + "shareQuota": { + "type": "int", + "defaultValue": 5120, + "metadata": { + "description": "Optional. The maximum size of the share, in gigabytes. Must be greater than 0, and less than or equal to 5120 (5TB). For Large File Shares, the maximum size is 102400 (100TB)." + } + }, + "enabledProtocols": { + "type": "string", + "defaultValue": "SMB", + "allowedValues": [ + "NFS", + "SMB" + ], + "metadata": { + "description": "Optional. The authentication protocol that is used for the file share. Can only be specified when creating a share." + } + }, + "rootSquash": { + "type": "string", + "defaultValue": "NoRootSquash", + "allowedValues": [ + "AllSquash", + "NoRootSquash", + "RootSquash" + ], + "metadata": { + "description": "Optional. Permissions for NFS file shares are enforced by the client OS rather than the Azure Files service. Toggling the root squash behavior reduces the rights of the root user for NFS shares." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "resources": { + "storageAccount::fileService": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('fileServicesName'))]", + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "fileShare": { + "type": "Microsoft.Storage/storageAccounts/fileServices/shares", + "apiVersion": "2023-01-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]", + "properties": { + "accessTier": "[parameters('accessTier')]", + "shareQuota": "[parameters('shareQuota')]", + "rootSquash": "[if(equals(parameters('enabledProtocols'), 'NFS'), parameters('rootSquash'), null())]", + "enabledProtocols": "[parameters('enabledProtocols')]" + }, + "dependsOn": [ + "storageAccount::fileService" + ] + }, + "fileShare_roleAssignments": { + "condition": "[not(empty(parameters('roleAssignments')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Share-Rbac', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "fileShareResourceId": { + "value": "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]" + }, + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "17818484349715313082" + } + }, + "parameters": { + "roleAssignments": { + "type": "array", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "fileShareResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the file share to assign the roles to." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "$fxv#0": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "scope": { + "type": "string", + "metadata": { + "description": "Required. The scope to deploy the role assignment to." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the role assignment." + } + }, + "roleDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. The role definition Id to assign." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User", + "" + ], + "defaultValue": "", + "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", + "allowedValues": [ + "2.0" + ], + "defaultValue": "2.0", + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[[parameters('scope')]", + "name": "[[parameters('name')]", + "properties": { + "roleDefinitionId": "[[parameters('roleDefinitionId')]", + "principalId": "[[parameters('principalId')]", + "description": "[[parameters('description')]", + "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())]" + } + } + ] + }, + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "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')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage File Data SMB Share Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb')]", + "Storage File Data SMB Share Elevated Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7')]", + "Storage File Data SMB Share Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": [ + { + "copy": { + "name": "fileShare_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('{0}-Share-Rbac-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "mode": "Incremental", + "expressionEvaluationOptions": { + "scope": "Outer" + }, + "template": "[variables('$fxv#0')]", + "parameters": { + "scope": { + "value": "[replace(parameters('fileShareResourceId'), '/shares/', '/fileShares/')]" + }, + "name": { + "value": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(parameters('fileShareResourceId'), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId, 'tyfa'))]" + }, + "roleDefinitionId": { + "value": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]" + }, + "principalId": { + "value": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]" + }, + "principalType": { + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]" + }, + "description": { + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]" + }, + "condition": { + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]" + }, + "conditionVersion": { + "value": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]" + }, + "delegatedManagedIdentityResourceId": { + "value": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + } + } + } + } + ] + } + }, + "dependsOn": [ + "fileShare" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "fileServices", + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share service." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/fileServices', parameters('storageAccountName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_queueServices": { + "condition": "[not(empty(parameters('queueServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-QueueServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('queueServices'), 'diagnosticSettings')]" + }, + "queues": { + "value": "[tryGet(parameters('queueServices'), 'queues')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "11962188257977966881" + }, + "name": "Storage Account Queue Services", + "description": "This module deploys a Storage Account Queue Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "queues": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Queues to create." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "name": "default" + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "queueServices": { + "type": "Microsoft.Storage/storageAccounts/queueServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "properties": {}, + "dependsOn": [ + "storageAccount" + ] + }, + "queueServices_diagnosticSettings": { + "copy": { + "name": "queueServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/queueServices/{1}', parameters('storageAccountName'), variables('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "queueServices" + ] + }, + "queueServices_queues": { + "copy": { + "name": "queueServices_queues", + "count": "[length(coalesce(parameters('queues'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Queue-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "name": { + "value": "[coalesce(parameters('queues'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('queues'), createArray())[copyIndex()], 'metadata')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('queues'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "18160215842105253661" + }, + "name": "Storage Account Queues", + "description": "This module deploys a Storage Account Queue.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the storage queue to deploy." + } + }, + "metadata": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Required. A name-value pair that represents queue metadata." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "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')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Queue Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]", + "Storage Queue Data Message Processor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]", + "Storage Queue Data Message Sender": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a')]", + "Storage Queue Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::queueServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/queueServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "queue": { + "type": "Microsoft.Storage/storageAccounts/queueServices/queues", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]" + }, + "dependsOn": [ + "storageAccount::queueServices" + ] + }, + "queue_roleAssignments": { + "copy": { + "name": "queue_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/queueServices/{1}/queues/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/queueServices/queues', parameters('storageAccountName'), 'default', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "queue" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed queue." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed queue." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/queueServices/queues', parameters('storageAccountName'), 'default', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed queue." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share service." + }, + "value": "[variables('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/queueServices', parameters('storageAccountName'), variables('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_tableServices": { + "condition": "[not(empty(parameters('tableServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-TableServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('tableServices'), 'diagnosticSettings')]" + }, + "tables": { + "value": "[tryGet(parameters('tableServices'), 'tables')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "11825608650296439803" + }, + "name": "Storage Account Table Services", + "description": "This module deploys a Storage Account Table Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "tables": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. tables to create." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "name": "default" + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "tableServices": { + "type": "Microsoft.Storage/storageAccounts/tableServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "properties": {}, + "dependsOn": [ + "storageAccount" + ] + }, + "tableServices_diagnosticSettings": { + "copy": { + "name": "tableServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/tableServices/{1}', parameters('storageAccountName'), variables('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "tableServices" + ] + }, + "tableServices_tables": { + "copy": { + "name": "tableServices_tables", + "count": "[length(parameters('tables'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Table-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('tables')[copyIndex()].name]" + }, + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "roleAssignments": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "16189309416113928106" + }, + "name": "Storage Account Table", + "description": "This module deploys a Storage Account Table.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the table." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "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')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Table Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]", + "Storage Table Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76199698-9eea-4c19-bc75-cec21354c6b6')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::tableServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/tableServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "table": { + "type": "Microsoft.Storage/storageAccounts/tableServices/tables", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "dependsOn": [ + "storageAccount::tableServices" + ] + }, + "table_roleAssignments": { + "copy": { + "name": "table_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/tableServices/{1}/tables/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/tableServices/tables', parameters('storageAccountName'), 'default', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "table" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share service." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/tableServices/tables', parameters('storageAccountName'), 'default', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed table service." + }, + "value": "[variables('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed table service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/tableServices', parameters('storageAccountName'), variables('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed table service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-secrets-kv', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '////'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1'), createArray(createObject('name', parameters('secretsExportConfiguration').accessKey1, 'value', listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2022-09-01').keys[0].value)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'connectionString1'), createArray(createObject('name', parameters('secretsExportConfiguration').connectionString1, 'value', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix=core.windows.net', parameters('name'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2022-09-01').keys[0]))), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2'), createArray(createObject('name', parameters('secretsExportConfiguration').accessKey2, 'value', listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2022-09-01').keys[1].value)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'connectionString2'), createArray(createObject('name', parameters('secretsExportConfiguration').connectionString2, 'value', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix=core.windows.net', parameters('name'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2022-09-01').keys[1]))), createArray()))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "986606208324987345" + } + }, + "definitions": { + "secretSetType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "secretToSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the ecrets in." + } + }, + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" + }, + "metadata": { + "description": "Required. The secrets to set in the Key Vault." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" + }, + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" + }, + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + }, + "dependsOn": [ + "keyVault" + ] + } + }, + "outputs": { + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetType" + }, + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]" + } + } + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed storage account." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed storage account." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed storage account." + }, + "value": "[resourceGroup().name]" + }, + "primaryBlobEndpoint": { + "type": "string", + "metadata": { + "description": "The primary blob endpoint reference if blob services are deployed." + }, + "value": "[if(and(not(empty(parameters('blobServices'))), contains(parameters('blobServices'), 'containers')), reference(format('Microsoft.Storage/storageAccounts/{0}', parameters('name')), '2019-04-01').primaryEndpoints.blob, '')]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('storageAccount', '2022-09-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('storageAccount', '2022-09-01', 'full').location]" + }, + "serviceEndpoints": { + "type": "object", + "metadata": { + "description": "All service endpoints of the deployed storage account, Note Standard_LRS and Standard_ZRS accounts only have a blob service endpoint." + }, + "value": "[reference('storageAccount').primaryEndpoints]" + }, + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + } + } + } + }, + "dependsOn": [ + "deploymentScriptPrivateDNSZone", + "newVnet", + "userAssignedIdentity" + ] + }, + "runPlaceHolderAgent": { + "condition": "[and(contains(parameters('computeTypes'), 'azure-container-app'), equals(parameters('selfHostedConfig').selfHostedType, 'azuredevops'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('runPlaceHolderAgent-{0}', uniqueString(resourceGroup().id))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('runPlaceHolderAgent-{0}', uniqueString(resourceGroup().id))]" + }, + "kind": { + "value": "AzurePowerShell" + }, + "azPowerShellVersion": { + "value": "12.1" + }, + "cleanupPreference": { + "value": "Always" + }, + "retentionInterval": { + "value": "P1D" + }, + "location": { + "value": "[parameters('location')]" + }, + "managedIdentities": { + "value": { + "userAssignedResourcesIds": [ + "[reference('userAssignedIdentity').outputs.resourceId.value]" + ] + } + }, + "storageAccountResourceId": "[if(parameters('privateNetworking'), createObject('value', reference('deploymentScriptStg').outputs.resourceId.value), createObject('value', null()))]", + "subnetResourceIds": "[if(and(parameters('privateNetworking'), equals(parameters('networkingConfiguration').networkType, 'createNew')), createObject('value', createArray(filter(reference('newVnet').outputs.subnetResourceIds.value, lambda('subnetId', contains(lambdaVariables('subnetId'), coalesce(tryGet(parameters('networkingConfiguration'), 'containerInstanceSubnetName'), 'aci-subnet'))))[0])), if(and(parameters('privateNetworking'), equals(parameters('networkingConfiguration').networkType, 'useExisting')), createObject('value', createArray(format('{0}/subnets/{1}', parameters('networkingConfiguration').virtualNetworkResourceId, parameters('networkingConfiguration').computeNetworking.containerInstanceSubnetName))), createObject('value', null())))]", + "arguments": { + "value": "[format('-resourceGroup {0} -jobName {1} -subscriptionId {2}', resourceGroup().name, reference('acaPlaceholderJob').outputs.name.value, subscription().subscriptionId)]" + }, + "scriptContent": { + "value": "[variables('$fxv#0')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "10563518610969019714" + }, + "name": "Deployment Scripts", + "description": "This module deploys Deployment Scripts.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "managedIdentitiesType": { + "type": "object", + "properties": { + "userAssignedResourcesIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "environmentVariableType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the environment variable." + } + }, + "secureValue": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Required. The value of the secure environment variable." + } + }, + "value": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. The value of the environment variable." + } + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 90, + "metadata": { + "description": "Required. Name of the Deployment Script." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AzureCLI", + "AzurePowerShell" + ], + "metadata": { + "description": "Required. Specifies the Kind of the Deployment Script." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "azPowerShellVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Azure PowerShell module version to be used. See a list of supported Azure PowerShell versions: https://mcr.microsoft.com/v2/azuredeploymentscripts-powershell/tags/list." + } + }, + "azCliVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Azure CLI module version to be used. See a list of supported Azure CLI versions: https://mcr.microsoft.com/v2/azure-cli/tags/list." + } + }, + "scriptContent": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Script body. Max length: 32000 characters. To run an external script, use primaryScriptURI instead." + } + }, + "primaryScriptUri": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Uri for the external script. This is the entry point for the external script. To run an internal script, use the scriptContent parameter instead." + } + }, + "environmentVariables": { + "type": "array", + "items": { + "$ref": "#/definitions/environmentVariableType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The environment variables to pass over to the script." + } + }, + "supportingScriptUris": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. List of supporting files for the external script (defined in primaryScriptUri). Does not work with internal scripts (code defined in scriptContent)." + } + }, + "subnetResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of subnet IDs to use for the container group. This is required if you want to run the deployment script in a private network. When using a private network, the `Storage File Data Privileged Contributor` role needs to be assigned to the user-assigned managed identity and the deployment principal needs to have permissions to list the storage account keys. Also, Shared-Keys must not be disabled on the used storage account [ref](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/deployment-script-vnet)." + } + }, + "arguments": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Command-line arguments to pass to the script. Arguments are separated by spaces." + } + }, + "retentionInterval": { + "type": "string", + "defaultValue": "P1D", + "metadata": { + "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)." + } + }, + "baseTime": { + "type": "string", + "defaultValue": "[utcNow('yyyy-MM-dd-HH-mm-ss')]", + "metadata": { + "description": "Generated. Do not provide a value! This date value is used to make sure the script run every time the template is deployed." + } + }, + "runOnce": { + "type": "bool", + "defaultValue": false, + "metadata": { + "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." + } + }, + "cleanupPreference": { + "type": "string", + "defaultValue": "Always", + "allowedValues": [ + "Always", + "OnSuccess", + "OnExpiration" + ], + "metadata": { + "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)." + } + }, + "containerGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "storageAccountResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "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." + } + }, + "timeout": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + }, + { + "name": "subnetIds", + "count": "[length(coalesce(parameters('subnetResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('subnetResourceIds'), createArray())[copyIndex('subnetIds')]]" + } + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + }, + "containerSettings": { + "containerGroupName": "[parameters('containerGroupName')]", + "subnetIds": "[if(not(empty(coalesce(variables('subnetIds'), createArray()))), variables('subnetIds'), null())]" + }, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourcesIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourcesIds'), createObject()))), 'UserAssigned', null()), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]" + }, + "resources": { + "storageAccount": { + "condition": "[not(empty(parameters('storageAccountResourceId')))]", + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2021-04-01", + "subscriptionId": "[split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '////'), '/')[4]]", + "name": "[last(split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), 'dummyAccount'), '/'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.resources-deploymentscript.{0}.{1}', replace('0.3.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "deploymentScript": { + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2023-08-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "kind": "[parameters('kind')]", + "properties": { + "azPowerShellVersion": "[if(equals(parameters('kind'), 'AzurePowerShell'), parameters('azPowerShellVersion'), null())]", + "azCliVersion": "[if(equals(parameters('kind'), 'AzureCLI'), parameters('azCliVersion'), null())]", + "containerSettings": "[if(not(empty(variables('containerSettings'))), variables('containerSettings'), null())]", + "storageAccountSettings": "[if(not(empty(parameters('storageAccountResourceId'))), if(not(empty(parameters('storageAccountResourceId'))), createObject('storageAccountKey', if(empty(parameters('subnetResourceIds')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '//'), '/')[2], split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), 'dummyAccount'), '/'))), '2023-01-01').keys[0].value, null()), 'storageAccountName', last(split(parameters('storageAccountResourceId'), '/'))), null()), null())]", + "arguments": "[parameters('arguments')]", + "environmentVariables": "[parameters('environmentVariables')]", + "scriptContent": "[if(not(empty(parameters('scriptContent'))), parameters('scriptContent'), null())]", + "primaryScriptUri": "[if(not(empty(parameters('primaryScriptUri'))), parameters('primaryScriptUri'), null())]", + "supportingScriptUris": "[if(not(empty(parameters('supportingScriptUris'))), parameters('supportingScriptUris'), null())]", + "cleanupPreference": "[parameters('cleanupPreference')]", + "forceUpdateTag": "[if(parameters('runOnce'), resourceGroup().name, parameters('baseTime'))]", + "retentionInterval": "[parameters('retentionInterval')]", + "timeout": "[parameters('timeout')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "deploymentScript_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Resources/deploymentScripts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "deploymentScript" + ] + }, + "deploymentScript_roleAssignments": { + "copy": { + "name": "deploymentScript_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Resources/deploymentScripts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Resources/deploymentScripts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "deploymentScript" + ] + }, + "deploymentScriptLogs": { + "existing": true, + "type": "Microsoft.Resources/deploymentScripts/logs", + "apiVersion": "2023-08-01", + "name": "[format('{0}/{1}', parameters('name'), 'default')]", + "dependsOn": [ + "deploymentScript" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployment script." + }, + "value": "[resourceId('Microsoft.Resources/deploymentScripts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the deployment script was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployment script." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('deploymentScript', '2023-08-01', 'full').location]" + }, + "outputs": { + "type": "object", + "metadata": { + "description": "The output of the deployment script." + }, + "value": "[coalesce(tryGet(reference('deploymentScript'), 'outputs'), createObject())]" + }, + "deploymentScriptLogs": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The logs of the deployment script." + }, + "value": "[split(reference('deploymentScriptLogs').log, '\n')]" + } + } + } + }, + "dependsOn": [ + "acaPlaceholderJob", + "deploymentScriptStg", + "newVnet", + "userAssignedIdentity" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the module was deployed to." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the module was deployed to." + }, + "value": "[parameters('location')]" + } + } +} \ No newline at end of file diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aca/README.md b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aca/README.md new file mode 100644 index 0000000000..70ef8545fc --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aca/README.md @@ -0,0 +1,30 @@ +# Linux Azure DevOps Agent Docker Image for Azure Container Apps + +> Note: You can update the [Dockerfile](dockerfile) to add any software that your require into the Azure DevOps Agent, if you don't want to have to download the bits during all pipelines executions. + +This docker file is used as a basic image for the Azure Verified Module to run Azure DevOps Agents in Azure Container Apps. + +## Credits + +The original Dockerfile and shell scripts are derived from [Microsoft Learn](https://learn.microsoft.com/en-us/azure/devops/pipelines/agents/docker?view=azure-devops#linux). + +## Environment Variables + +`AZP_URL`: The URL of the Azure DevOps organization. +`AZP_TOKEN`: The token used to authenticate the runner with Azure DevOps. This is a PAT (Personal Access Token) with the relevant scopes that requires a long expiration date. +`AZP_POOL`: The name of the agent pool. +`AZP_AGENT_NAME`: The name of the agent. +`AZP_AGENT_NAME_PREFIX`: The prefix for the agent name. Overrides `AZP_AGENT_NAME`. +`AZP_RANDOM_AGENT_SUFFIX`: Whether to add a random string to the `AZP_AGENT_NAME_PREFIX` to create a unique agent name. Default is `true`. + +## Build it + +```bash +docker build -t YOUR_IMAGE_NAME:YOUR_IMAGE_TAG . +``` + +## Push it + +```bash +docker push YOUR_IMAGE_NAME:YOUR_IMAGE_TAG +``` diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aca/dockerfile b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aca/dockerfile new file mode 100644 index 0000000000..93fe9d943b --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aca/dockerfile @@ -0,0 +1,39 @@ +FROM mcr.microsoft.com/azure-powershell:ubuntu-22.04 +ENV TARGETARCH="linux-x64" + +# To make it easier for build and release pipelines to run apt-get, +# configure apt to not require confirmation (assume the -y argument by default) +ENV DEBIAN_FRONTEND=noninteractive +RUN echo "APT::Get::Assume-Yes \"true\";" > /etc/apt/apt.conf.d/90assumeyes + +RUN apt-get update \ +&& apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + jq \ + git \ + iputils-ping \ + libicu70 \ + libcurl4 \ + libunwind8 \ + netcat \ + ruby \ + unzip \ + dnsutils + +RUN curl -sL https://aka.ms/InstallAzureCLIDeb | bash + +WORKDIR /azp/ + +COPY ./start.sh ./ +RUN chmod +x ./start.sh + +# Create agent user and set up home directory +RUN useradd -m -d /home/agent agent +RUN chown -R agent:agent /azp /home/agent + +USER agent +# Another option is to run the agent as root. +# ENV AGENT_ALLOW_RUNASROOT="true" + +ENTRYPOINT [ "./start.sh" ] \ No newline at end of file diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aca/start.sh b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aca/start.sh new file mode 100644 index 0000000000..16296edc40 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aca/start.sh @@ -0,0 +1,115 @@ +#!/bin/bash +set -e + +if [ -z "${AZP_URL}" ]; then + echo 1>&2 "error: missing AZP_URL environment variable" + exit 1 +fi + +if [ -z "${AZP_TOKEN_FILE}" ]; then + if [ -z "${AZP_TOKEN}" ]; then + echo 1>&2 "error: missing AZP_TOKEN environment variable" + exit 1 + fi + + AZP_TOKEN_FILE="/azp/.token" + echo -n "${AZP_TOKEN}" > "${AZP_TOKEN_FILE}" +fi + +unset AZP_TOKEN + +if [ -n "${AZP_WORK}" ]; then + mkdir -p "${AZP_WORK}" +fi + +export AGENT_ALLOW_RUNASROOT="1" + +cleanup() { + # If $AZP_PLACEHOLDER is set, skip cleanup + if [ -n "$AZP_PLACEHOLDER" ]; then + echo 'Running in placeholder mode, skipping cleanup' + else + if [ -e ./config.sh ]; then + print_header "Cleanup. Removing Azure Pipelines agent..." + + # If the agent has some running jobs, the configuration removal process will fail. + # So, give it some time to finish the job. + while true; do + ./config.sh remove --unattended --auth "PAT" --token $(cat "${AZP_TOKEN_FILE}") && break + + echo "Retrying in 30 seconds..." + sleep 30 + done + fi + fi +} + +print_header() { + lightcyan="\033[1;36m" + nocolor="\033[0m" + echo -e "\n${lightcyan}$1${nocolor}\n" +} + +# Let the agent ignore the token env variables +export VSO_AGENT_IGNORE="AZP_TOKEN,AZP_TOKEN_FILE" + +print_header "1. Determining matching Azure Pipelines agent..." + +AZP_AGENT_PACKAGES=$(curl -LsS \ + -u user:$(cat "${AZP_TOKEN_FILE}") \ + -H "Accept:application/json;" \ + "${AZP_URL}/_apis/distributedtask/packages/agent?platform=${TARGETARCH}&top=1") + +AZP_AGENT_PACKAGE_LATEST_URL=$(echo "${AZP_AGENT_PACKAGES}" | jq -r ".value[0].downloadUrl") + +if [ -z "${AZP_AGENT_PACKAGE_LATEST_URL}" -o "${AZP_AGENT_PACKAGE_LATEST_URL}" == "null" ]; then + echo 1>&2 "error: could not determine a matching Azure Pipelines agent" + echo 1>&2 "check that account "${AZP_URL}" is correct and the token is valid for that account" + exit 1 +fi + +print_header "2. Downloading and extracting Azure Pipelines agent..." + +echo "Agent package URL: ${AZP_AGENT_PACKAGE_LATEST_URL}" + +curl -LsS "${AZP_AGENT_PACKAGE_LATEST_URL}" | tar -xz & wait $! + +source ./env.sh + +trap "cleanup; exit 0" EXIT +trap "cleanup; exit 130" INT +trap "cleanup; exit 143" TERM + +print_header "3. Configuring Azure Pipelines agent..." + +_RANDOM_AGENT_SUFFIX=${AZP_RANDOM_AGENT_SUFFIX:="true"} +_AGENT_NAME=${AZP_AGENT_NAME:-${AZP_AGENT_NAME_PREFIX:-azure-devops-agent}-$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13 ; echo '')} +if [[ ${_RANDOM_AGENT_SUFFIX} != "true" ]]; then + _AGENT_NAME=${AZP_AGENT_NAME:-${AZP_AGENT_NAME_PREFIX:-azure-devops-agent}-$(hostname)} + echo "AZP_RANDOM_AGENT_SUFFIX is ${AZP_RANDOM_AGENT_SUFFIX}. Setting agent name to ${_AGENT_NAME}" +fi + +echo "Agent name: ${_AGENT_NAME}" + +./config.sh --unattended \ + --agent "${_AGENT_NAME}" \ + --url "${AZP_URL}" \ + --auth "PAT" \ + --token $(cat "${AZP_TOKEN_FILE}") \ + --pool "${AZP_POOL:-Default}" \ + --work "${AZP_WORK:-_work}" \ + --replace \ + --acceptTeeEula & wait $! + +print_header "4. Running Azure Pipelines agent..." + +chmod +x ./run.sh + +# If $AZP_PLACEHOLDER is set, skipping running the agent +if [ -n "$AZP_PLACEHOLDER" ]; then + echo 'Running in placeholder mode, skipping running the agent' +else + # To be aware of TERM and INT signals call ./run.sh + # Running it with the --once flag at the end will shut down the agent after the build is executed + ./run.sh "$@" --once & wait $! +fi \ No newline at end of file diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aci/README.md b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aci/README.md new file mode 100644 index 0000000000..1d3174e821 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aci/README.md @@ -0,0 +1,19 @@ +# Linux Azure DevOps Agent Docker Image for Azure Container Instances + +IMPORTANT: This code is completely stolen from https://github.com/Azure/terraform-azurerm-aci-devops-agent + +This image is based on the [official documentation](https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/docker?view=azure-devops#linux). + +> Note: You can update the [Dockerfile](Dockerfile) to add any software that your require into the Azure DevOps agent, if you don't want to have to download the bits during all pipelines executions. + +## Build it + +```bash +docker build -t YOUR_IMAGE_NAME:YOUR_IMAGE_TAG . +``` + +## Push it + +```bash +docker push YOUR_IMAGE_NAME:YOUR_IMAGE_TAG +``` diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aci/dockerfile b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aci/dockerfile new file mode 100644 index 0000000000..f1a2574b6a --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aci/dockerfile @@ -0,0 +1,29 @@ +FROM mcr.microsoft.com/azure-powershell:ubuntu-22.04 + +# To make it easier for build and release pipelines to run apt-get, +# configure apt to not require confirmation (assume the -y argument by default) +ENV DEBIAN_FRONTEND=noninteractive +RUN echo "APT::Get::Assume-Yes \"true\";" > /etc/apt/apt.conf.d/90assumeyes + +RUN apt-get update \ +&& apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + jq \ + git \ + iputils-ping \ + libcurl4 \ + libunwind8 \ + netcat \ + ruby \ + unzip \ + dnsutils + +RUN curl -sL https://aka.ms/InstallAzureCLIDeb | bash + +WORKDIR /azp + +COPY ./start.sh . +RUN chmod +x start.sh + +CMD ["./start.sh"] \ No newline at end of file diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aci/start.sh b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aci/start.sh new file mode 100644 index 0000000000..bdee34e733 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/azure-devops-agent-aci/start.sh @@ -0,0 +1,98 @@ +#!/bin/bash +set -e + +if [ -z "$AZP_URL" ]; then + echo 1>&2 "error: missing AZP_URL environment variable" + exit 1 +fi + +if [ -z "$AZP_TOKEN_FILE" ]; then + if [ -z "$AZP_TOKEN" ]; then + echo 1>&2 "error: missing AZP_TOKEN environment variable" + exit 1 + fi + + AZP_TOKEN_FILE=/azp/.token + echo -n $AZP_TOKEN > "$AZP_TOKEN_FILE" +fi + +unset AZP_TOKEN + +if [ -n "$AZP_WORK" ]; then + mkdir -p "$AZP_WORK" +fi + +rm -rf /azp/agent +mkdir /azp/agent +cd /azp/agent + +export AGENT_ALLOW_RUNASROOT="1" + +cleanup() { + if [ -e config.sh ]; then + print_header "Cleanup. Removing Azure Pipelines agent..." + + ./config.sh remove --unattended \ + --auth PAT \ + --token $(cat "$AZP_TOKEN_FILE") + fi +} + +print_header() { + lightcyan='\033[1;36m' + nocolor='\033[0m' + echo -e "${lightcyan}$1${nocolor}" +} + +# Let the agent ignore the token env variables +export VSO_AGENT_IGNORE=AZP_TOKEN,AZP_TOKEN_FILE + +# ACI in private vnet require time to get access to Internet +sleep 30 + +print_header "1. Determining matching Azure Pipelines agent..." + +AZP_AGENT_RESPONSE=$(curl -LsS \ + -u user:$(cat "$AZP_TOKEN_FILE") \ + -H 'Accept:application/json;api-version=3.0-preview' \ + "$AZP_URL/_apis/distributedtask/packages/agent?platform=linux-x64") + +if echo "$AZP_AGENT_RESPONSE" | jq . >/dev/null 2>&1; then + AZP_AGENTPACKAGE_URL=$(echo "$AZP_AGENT_RESPONSE" \ + | jq -r '.value | map([.version.major,.version.minor,.version.patch,.downloadUrl]) | sort | .[length-1] | .[3]') +fi + +if [ -z "$AZP_AGENTPACKAGE_URL" -o "$AZP_AGENTPACKAGE_URL" == "null" ]; then + echo 1>&2 "error: could not determine a matching Azure Pipelines agent - check that account '$AZP_URL' is correct and the token is valid for that account" + exit 1 +fi + +print_header "2. Downloading and installing Azure Pipelines agent..." + +curl -LsS $AZP_AGENTPACKAGE_URL | tar -xz & wait $! + +source ./env.sh + +trap 'cleanup; exit 130' INT +trap 'cleanup; exit 143' TERM + +print_header "3. Configuring Azure Pipelines agent..." + +./config.sh --unattended \ + --agent "${AZP_AGENT_NAME:-$(hostname)}" \ + --url "$AZP_URL" \ + --auth PAT \ + --token $(cat "$AZP_TOKEN_FILE") \ + --pool "${AZP_POOL:-Default}" \ + --work "${AZP_WORK:-_work}" \ + --replace \ + --acceptTeeEula & wait $! + +# remove the administrative token before accepting work +rm $AZP_TOKEN_FILE + +print_header "4. Running Azure Pipelines agent..." + +# `exec` the node runtime so it's aware of TERM and INT signals +# AgentService.js understands how to handle agent self-update and restart +exec ./externals/node/bin/node ./bin/AgentService.js interactive --once \ No newline at end of file diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/README.md b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/README.md new file mode 100644 index 0000000000..843c854f6b --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/README.md @@ -0,0 +1,33 @@ +# Linux GitHub Runner Docker Image for Azure Container Apps + +> Note: You can update the [Dockerfile](dockerfile) to add any software that your require into the GitHub Runner, if you don't want to have to download the bits during all pipelines executions. + +This docker file is used as a basic image for the Azure Verified Module to run GitHub Actions in Azure Container Apps. + +## Credits + +The original Dockerfile and shell scripts are derived from [myoung34/docker-github-actions-runner](https://github.com/myoung34/docker-github-actions-runner). + +## Environment Variables + +`REPO_URL`: The URL of the repository to add the runner to. +`ORG_NAME`: The name of the organization that the repository belongs to. +`ENTERPRISE_NAME`: The name of the enterprise that the repository belongs to. +`ACCESS_TOKEN`: The token used to authenticate the runner with GitHub. This is a PAT (Personal Access Token) with the relevant scopes that requires a long expiration date. +`RUNNER_NAME_PREFIX`: The prefix for the runner name. +`RANDOM_RUNNER_SUFFIX`: Whether to add a random string to the RUNNER_NAME_PREFIX to create a unique runner name. Default is `true`. +`RUNNER_SCOPE`: The scope of the runner. Valid values are `repo`, `org` and `ent`. +`RUNNER_GROUP`: The runner group if using them. +`EPHEMERAL`: Whether the runner is ephemeral or not. + +## Build it + +```bash +docker build --progress=plain --no-cache -t YOUR_IMAGE_NAME:YOUR_IMAGE_TAG . +``` + +## Push it + +```bash +docker push YOUR_IMAGE_NAME:YOUR_IMAGE_TAG +``` diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/app_token.sh b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/app_token.sh new file mode 100644 index 0000000000..8c32384bfc --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/app_token.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# +# Request an ACCESS_TOKEN to be used by a GitHub APP +# Environment variable that need to be set up: +# * APP_ID, the GitHub's app ID +# * APP_PRIVATE_KEY, the content of GitHub app's private key in PEM format. +# * APP_LOGIN, the login name used to install GitHub's app +# +# https://github.com/orgs/community/discussions/24743#discussioncomment-3245300 +# + +set -o pipefail + +_GITHUB_HOST=${GITHUB_HOST:="github.com"} + +# If URL is not github.com then use the enterprise api endpoint +if [[ ${GITHUB_HOST} = "github.com" ]]; then + URI="https://api.${_GITHUB_HOST}" +else + URI="https://${_GITHUB_HOST}/api/v3" +fi + +API_VERSION=v3 +API_HEADER="Accept: application/vnd.github.${API_VERSION}+json" +CONTENT_LENGTH_HEADER="Content-Length: 0" +APP_INSTALLATIONS_URI="${URI}/app/installations" + + +# JWT parameters based off +# https://docs.github.com/en/developers/apps/building-github-apps/authenticating-with-github-apps#authenticating-as-a-github-app +# +# JWT token issuance and expiration parameters +JWT_IAT_DRIFT=60 +JWT_EXP_DELTA=600 + +JWT_JOSE_HEADER='{ + "alg": "RS256", + "typ": "JWT" +}' + + +build_jwt_payload() { + now=$(date +%s) + iat=$((now - JWT_IAT_DRIFT)) + jq -c \ + --arg iat_str "${iat}" \ + --arg exp_delta_str "${JWT_EXP_DELTA}" \ + --arg app_id_str "${APP_ID}" \ + ' + ($iat_str | tonumber) as $iat + | ($exp_delta_str | tonumber) as $exp_delta + | ($app_id_str | tonumber) as $app_id + | .iat = $iat + | .exp = ($iat + $exp_delta) + | .iss = $app_id + ' <<< "{}" | tr -d '\n' +} + +base64url() { + base64 | tr '+/' '-_' | tr -d '=\n' +} + +rs256_sign() { + openssl dgst -binary -sha256 -sign <(echo "$1") +} + +request_access_token() { + jwt_payload=$(build_jwt_payload) + encoded_jwt_parts=$(base64url <<<"${JWT_JOSE_HEADER}").$(base64url <<<"${jwt_payload}") + encoded_mac=$(echo -n "${encoded_jwt_parts}" | rs256_sign "${APP_PRIVATE_KEY}" | base64url) + generated_jwt="${encoded_jwt_parts}.${encoded_mac}" + + auth_header="Authorization: Bearer ${generated_jwt}" + + app_installations_response=$(curl -sX GET \ + -H "${auth_header}" \ + -H "${API_HEADER}" \ + "${APP_INSTALLATIONS_URI}" \ + ) + access_token_url=$(echo "${app_installations_response}" | jq --raw-output '.[] | select (.account.login == "'"${APP_LOGIN}"'" and .app_id == '"${APP_ID}"') .access_tokens_url') + curl -sX POST \ + -H "${CONTENT_LENGTH_HEADER}" \ + -H "${auth_header}" \ + -H "${API_HEADER}" \ + "${access_token_url}" | \ + jq --raw-output .token +} + +request_access_token diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/dockerfile b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/dockerfile new file mode 100644 index 0000000000..00caa92e68 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/dockerfile @@ -0,0 +1,49 @@ +FROM mcr.microsoft.com/azure-powershell:ubuntu-22.04 + +# To make it easier for build and release pipelines to run apt-get, +# configure apt to not require confirmation (assume the -y argument by default) +ENV DEBIAN_FRONTEND=noninteractive +RUN echo "APT::Get::Assume-Yes \"true\";" > /etc/apt/apt.conf.d/90assumeyes + +RUN apt-get update \ +&& apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + jq \ + git \ + dumb-init \ + sudo \ + iputils-ping \ + libcurl4 \ + libunwind8 \ + netcat \ + ruby \ + unzip \ + dnsutils \ + nodejs + +RUN curl -sL https://aka.ms/InstallAzureCLIDeb | bash + +RUN groupadd -g 121 runner \ + && useradd -mr -d /home/runner -u 1001 -g 121 runner \ + && usermod -aG sudo runner \ + && echo '%sudo ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers + +ENV AGENT_TOOLSDIRECTORY=/opt/hostedtoolcache +RUN mkdir -p /opt/hostedtoolcache + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +WORKDIR /actions-runner +COPY install_actions.sh /actions-runner + +RUN chmod +x /actions-runner/install_actions.sh \ + && /actions-runner/install_actions.sh ${GH_RUNNER_VERSION} ${TARGETPLATFORM} \ + && rm /actions-runner/install_actions.sh \ + && chown runner /_work /actions-runner /opt/hostedtoolcache + +COPY token.sh entrypoint.sh app_token.sh / +RUN chmod +x /token.sh /entrypoint.sh /app_token.sh + +ENTRYPOINT ["/entrypoint.sh"] +CMD ["./bin/Runner.Listener", "run", "--startuptype", "service"] \ No newline at end of file diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/entrypoint.sh b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/entrypoint.sh new file mode 100644 index 0000000000..5bdda1bb76 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/entrypoint.sh @@ -0,0 +1,212 @@ +#!/usr/bin/dumb-init /bin/bash +# shellcheck shell=bash + +export RUNNER_ALLOW_RUNASROOT=1 +export PATH=${PATH}:/actions-runner + +# Un-export these, so that they must be passed explicitly to the environment of +# any command that needs them. This may help prevent leaks. +export -n ACCESS_TOKEN +export -n RUNNER_TOKEN +export -n APP_ID +export -n APP_PRIVATE_KEY + +trap_with_arg() { + func="$1" ; shift + for sig ; do + trap "$func $sig" "$sig" + done +} + +deregister_runner() { + echo "Caught $1 - Deregistering runner" + if [[ -n "${ACCESS_TOKEN}" ]]; then + _TOKEN=$(ACCESS_TOKEN="${ACCESS_TOKEN}" bash /token.sh) + RUNNER_TOKEN=$(echo "${_TOKEN}" | jq -r .token) + fi + ./config.sh remove --token "${RUNNER_TOKEN}" + exit +} + +_DISABLE_AUTOMATIC_DEREGISTRATION=${DISABLE_AUTOMATIC_DEREGISTRATION:-false} + +_RANDOM_RUNNER_SUFFIX=${RANDOM_RUNNER_SUFFIX:="true"} + +_RUNNER_NAME=${RUNNER_NAME:-${RUNNER_NAME_PREFIX:-github-runner}-$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13 ; echo '')} +if [[ ${RANDOM_RUNNER_SUFFIX} != "true" ]]; then + # In some cases this file does not exist + if [[ -f "/etc/hostname" ]]; then + # in some cases it can also be empty + if [[ $(stat --printf="%s" /etc/hostname) -ne 0 ]]; then + _RUNNER_NAME=${RUNNER_NAME:-${RUNNER_NAME_PREFIX:-github-runner}-$(cat /etc/hostname)} + echo "RANDOM_RUNNER_SUFFIX is ${RANDOM_RUNNER_SUFFIX}. /etc/hostname exists and has content. Setting runner name to ${_RUNNER_NAME}" + else + echo "RANDOM_RUNNER_SUFFIX is ${RANDOM_RUNNER_SUFFIX} ./etc/hostname exists but is empty. Not using /etc/hostname." + fi + else + echo "RANDOM_RUNNER_SUFFIX is ${RANDOM_RUNNER_SUFFIX} but /etc/hostname does not exist. Not using /etc/hostname." + fi +fi + +_RUNNER_WORKDIR=${RUNNER_WORKDIR:-/_work/${_RUNNER_NAME}} +_LABELS=${LABELS:-default} +_RUNNER_GROUP=${RUNNER_GROUP:-Default} +_GITHUB_HOST=${GITHUB_HOST:="github.com"} +_RUN_AS_ROOT=${RUN_AS_ROOT:="true"} +_START_DOCKER_SERVICE=${START_DOCKER_SERVICE:="false"} + +# ensure backwards compatibility +if [[ -z ${RUNNER_SCOPE} ]]; then + if [[ ${ORG_RUNNER} == "true" ]]; then + echo 'ORG_RUNNER is now deprecated. Please use RUNNER_SCOPE="org" instead.' + export RUNNER_SCOPE="org" + else + export RUNNER_SCOPE="repo" + fi +fi + +RUNNER_SCOPE="${RUNNER_SCOPE,,}" # to lowercase + +case ${RUNNER_SCOPE} in + org*) + [[ -z ${ORG_NAME} ]] && ( echo "ORG_NAME required for org runners"; exit 1 ) + _SHORT_URL="https://${_GITHUB_HOST}/${ORG_NAME}" + RUNNER_SCOPE="org" + if [[ -n "${APP_ID}" ]] && [[ -z "${APP_LOGIN}" ]]; then + APP_LOGIN=${ORG_NAME} + fi + ;; + + ent*) + [[ -z ${ENTERPRISE_NAME} ]] && ( echo "ENTERPRISE_NAME required for enterprise runners"; exit 1 ) + _SHORT_URL="https://${_GITHUB_HOST}/enterprises/${ENTERPRISE_NAME}" + RUNNER_SCOPE="enterprise" + ;; + + *) + [[ -z ${REPO_URL} ]] && ( echo "REPO_URL required for repo runners"; exit 1 ) + _SHORT_URL=${REPO_URL} + RUNNER_SCOPE="repo" + if [[ -n "${APP_ID}" ]] && [[ -z "${APP_LOGIN}" ]]; then + APP_LOGIN=${REPO_URL%/*} + APP_LOGIN=${APP_LOGIN##*/} + fi + ;; +esac + +configure_runner() { + ARGS=() + if [[ -n "${APP_ID}" ]] && [[ -n "${APP_PRIVATE_KEY}" ]] && [[ -n "${APP_LOGIN}" ]]; then + if [[ -n "${ACCESS_TOKEN}" ]] || [[ -n "${RUNNER_TOKEN}" ]]; then + echo "ERROR: ACCESS_TOKEN or RUNNER_TOKEN provided but are mutually exclusive with APP_ID, APP_PRIVATE_KEY and APP_LOGIN." >&2 + exit 1 + fi + echo "Obtaining access token for app_id ${APP_ID} and login ${APP_LOGIN}" + nl=" +" + ACCESS_TOKEN=$(APP_ID="${APP_ID}" APP_PRIVATE_KEY="${APP_PRIVATE_KEY//\\n/${nl}}" APP_LOGIN="${APP_LOGIN}" bash /app_token.sh) + elif [[ -n "${APP_ID}" ]] || [[ -n "${APP_PRIVATE_KEY}" ]] || [[ -n "${APP_LOGIN}" ]]; then + echo "ERROR: All of APP_ID, APP_PRIVATE_KEY and APP_LOGIN must be specified." >&2 + exit 1 + fi + + if [[ -n "${ACCESS_TOKEN}" ]]; then + echo "Obtaining the token of the runner" + _TOKEN=$(ACCESS_TOKEN="${ACCESS_TOKEN}" bash /token.sh) + RUNNER_TOKEN=$(echo "${_TOKEN}" | jq -r .token) + fi + + # shellcheck disable=SC2153 + if [ -n "${EPHEMERAL}" ]; then + echo "Ephemeral option is enabled" + ARGS+=("--ephemeral") + fi + + if [ -n "${DISABLE_AUTO_UPDATE}" ]; then + echo "Disable auto update option is enabled" + ARGS+=("--disableupdate") + fi + + if [ -n "${NO_DEFAULT_LABELS}" ]; then + echo "Disable adding the default self-hosted, platform, and architecture labels" + ARGS+=("--no-default-labels") + fi + + echo "Configuring" + ./config.sh \ + --url "${_SHORT_URL}" \ + --token "${RUNNER_TOKEN}" \ + --name "${_RUNNER_NAME}" \ + --work "${_RUNNER_WORKDIR}" \ + --labels "${_LABELS}" \ + --runnergroup "${_RUNNER_GROUP}" \ + --unattended \ + --replace \ + "${ARGS[@]}" + + [[ ! -d "${_RUNNER_WORKDIR}" ]] && mkdir "${_RUNNER_WORKDIR}" + +} + + +# Opt into runner reusage because a value was given +if [[ -n "${CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" ]]; then + echo "Runner reusage is enabled" + + # directory exists, copy the data + if [[ -d "${CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" ]]; then + echo "Copying previous data" + cp -p -r "${CONFIGURED_ACTIONS_RUNNER_FILES_DIR}/." "/actions-runner" + fi + + if [ -f "/actions-runner/.runner" ]; then + echo "The runner has already been configured" + else + configure_runner + fi +else + echo "Runner reusage is disabled" + configure_runner +fi + +if [[ -n "${CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" ]]; then + echo "Reusage is enabled. Storing data to ${CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" + # Quoting (even with double-quotes) the regexp brokes the copying + cp -p -r "/actions-runner/_diag" "/actions-runner/svc.sh" /actions-runner/.[^.]* "${CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" +fi + + + +if [[ ${_DISABLE_AUTOMATIC_DEREGISTRATION} == "false" ]]; then + trap_with_arg deregister_runner SIGINT SIGQUIT SIGTERM INT TERM QUIT +fi + +# Start docker service if needed (e.g. for docker-in-docker) +if [[ ${_START_DOCKER_SERVICE} == "true" ]]; then + echo "Starting docker service" + _PREFIX="" + [[ ${_RUN_AS_ROOT} != "true" ]] && _PREFIX="sudo" + ${_PREFIX} service docker start +fi + +# Container's command (CMD) execution as runner user + + +if [[ ${_RUN_AS_ROOT} == "true" ]]; then + if [[ $(id -u) -eq 0 ]]; then + "$@" + else + echo "ERROR: RUN_AS_ROOT env var is set to true but the user has been overridden and is not running as root, but UID '$(id -u)'" + exit 1 + fi +else + if [[ $(id -u) -eq 0 ]]; then + [[ -n "${CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" ]] && chown -R runner "${CONFIGURED_ACTIONS_RUNNER_FILES_DIR}" + chown -R runner "${_RUNNER_WORKDIR}" /actions-runner + # The toolcache is not recursively chowned to avoid recursing over prepulated tooling in derived docker images + chown runner /opt/hostedtoolcache/ + /usr/sbin/gosu runner "$@" + else + "$@" + fi +fi diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/install_actions.sh b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/install_actions.sh new file mode 100644 index 0000000000..9c7f78401b --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/install_actions.sh @@ -0,0 +1,23 @@ +#!/bin/bash -e +GH_RUNNER_VERSION=$1 +TARGETPLATFORM=$2 + +export TARGET_ARCH="x64" +if [[ $TARGETPLATFORM == "linux/arm64" ]]; then + export TARGET_ARCH="arm64" +fi + +if [[ $GH_RUNNER_VERSION == "" ]]; then + LASTEST_VERSION=$(curl "https://api.github.com/repos/actions/runner/releases/latest") + GH_RUNNER_VERSION=$(echo $LASTEST_VERSION | jq -r '.tag_name') + echo "GitHub Runner latest version: $GH_RUNNER_VERSION" +fi + +ReplaceWith="" +GH_RUNNER_VERSION=${GH_RUNNER_VERSION/v/${ReplaceWith}} + +curl -L "https://github.com/actions/runner/releases/download/v${GH_RUNNER_VERSION}/actions-runner-linux-${TARGET_ARCH}-${GH_RUNNER_VERSION}.tar.gz" > actions.tar.gz +tar -zxf actions.tar.gz +rm -f actions.tar.gz +./bin/installdependencies.sh +mkdir /_work diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/token.sh b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/token.sh new file mode 100644 index 0000000000..64d2196696 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aca/token.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +_GITHUB_HOST=${GITHUB_HOST:="github.com"} + +# If URL is not github.com then use the enterprise api endpoint +if [[ ${GITHUB_HOST} = "github.com" ]]; then + URI="https://api.${_GITHUB_HOST}" +else + URI="https://${_GITHUB_HOST}/api/v3" +fi + +API_VERSION=v3 +API_HEADER="Accept: application/vnd.github.${API_VERSION}+json" +AUTH_HEADER="Authorization: token ${ACCESS_TOKEN}" +CONTENT_LENGTH_HEADER="Content-Length: 0" + +case ${RUNNER_SCOPE} in + org*) + _FULL_URL="${URI}/orgs/${ORG_NAME}/actions/runners/registration-token" + ;; + + ent*) + _FULL_URL="${URI}/enterprises/${ENTERPRISE_NAME}/actions/runners/registration-token" + ;; + + *) + _PROTO="https://" + # shellcheck disable=SC2116 + _URL="$(echo "${REPO_URL/${_PROTO}/}")" + _PATH="$(echo "${_URL}" | grep / | cut -d/ -f2-)" + _ACCOUNT="$(echo "${_PATH}" | cut -d/ -f1)" + _REPO="$(echo "${_PATH}" | cut -d/ -f2)" + _FULL_URL="${URI}/repos/${_ACCOUNT}/${_REPO}/actions/runners/registration-token" + ;; +esac + +RUNNER_TOKEN="$(curl -XPOST -fsSL \ + -H "${CONTENT_LENGTH_HEADER}" \ + -H "${AUTH_HEADER}" \ + -H "${API_HEADER}" \ + "${_FULL_URL}" \ +| jq -r '.token')" + +echo "{\"token\": \"${RUNNER_TOKEN}\", \"full_url\": \"${_FULL_URL}\"}" diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aci/ConfigureAndRun.ps1 b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aci/ConfigureAndRun.ps1 new file mode 100644 index 0000000000..53a6f167af --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aci/ConfigureAndRun.ps1 @@ -0,0 +1,65 @@ +$token = $env:GH_RUNNER_TOKEN +$url = $env:GH_RUNNER_URL +$runnerName = $env:GH_RUNNER_NAME +$runnerGroup = $env:GH_RUNNER_GROUP +$runnerMode = $env:GH_RUNNER_MODE + +$hasRunnerGroup = ($null -ne $runnerGroup -and $runnerGroup -ne "") +$isEphemeral = $true + +if($null -ne $runnerMode -and $runnerMode -ne "" -and $runnerMode.ToLower() -eq "persistent") { + $isEphemeral = $false +} + +# Get the runner registration token from the GitHub API if a PAT is supplied +$isPat = $false +if($token.StartsWith("ghp_") -or $token.StartsWith("github_pat_")) { + $isPat = $true +} + +if($isPat) { + $githubUrlSplit = $url.Split("/", [System.StringSplitOptions]::RemoveEmptyEntries) + $githubOrgRepoSegment = "" + $tokenApiUrl = "" + $tokenType = "" + + if($githubUrlSplit.Length -eq 3) { + $githubOrgRepoSegment = $githubUrlSplit[-1] + $tokenType = "orgs" + } else { + $githubOrgRepoSegment = $githubUrlSplit[-2] + "/" + $githubUrlSplit[-1] + $tokenType = "repos" + } + + $tokenApiUrl = "https://api.github.com/$($tokenType)/$($githubOrgRepoSegment)/actions/runners/registration-token" + + Write-Host "Generating a new runner registration token using the supplied PAT from the url $tokenApiUrl" + + $headers = @{} + $headers.Add("Authorization", "bearer $token") + $headers.Add("Accept", "application/vnd.github.v3+json") + + $token = (Invoke-RestMethod -Uri $tokenApiUrl -Headers $headers -Method Post).token +} + +# Register the runner +$env:RUNNER_ALLOW_RUNASROOT = "1" +if($hasRunnerGroup) { + if($isEphemeral) { + Write-Host "Registering the runner $runnerName with the runner group $runnerGroup and ephemeral mode" + ./config.sh --unattended --replace --url $url --token $token --name $runnerName --runnergroup $runnerGroup --ephemeral + } else { + Write-Host "Registering the runner $runnerName with the runner group $runnerGroup" + ./config.sh --unattended --replace --url $url --token $token --name $runnerName --runnergroup $runnerGroup + } +} else { + if($isEphemeral) { + Write-Host "Registering the runner $runnerName in ephemeral mode" + ./config.sh --unattended --replace --url $url --token $token --name $runnerName --ephemeral + } else { + Write-Host "Registering the runner $runnerName" + ./config.sh --unattended --replace --url $url --token $token --name $runnerName + } +} + +./run.sh diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aci/InstallAgent.ps1 b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aci/InstallAgent.ps1 new file mode 100644 index 0000000000..b22ca31f94 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aci/InstallAgent.ps1 @@ -0,0 +1,19 @@ +$releaseData = Invoke-RestMethod -Uri "https://api.github.com/repos/actions/runner/releases/latest" + +$releaseVersion = $releaseData.tag_name.Replace("v", "") + +$filter = "actions-runner-linux-x64-$releaseVersion.tar.gz" + +$releaseUrl = $releaseData.assets | Where-Object { $_.name -like $filter } | Select-Object 'browser_download_url' +$downloadUrl = $releaseUrl.browser_download_url + +Write-Output "Got Download Url $downloadUrl. Downloading now..." +$fileName = "actions-runner-latest.tar.gz" +Invoke-WebRequest -Uri $downloadUrl -OutFile $fileName | Out-String | Write-Verbose + +Write-Output "Got $fileName. Extracting now..." +tar xzf ./$fileName + +Write-Output "Extracted $fileName. Cleaning up now..." +Remove-Item $fileName -Force ; +Write-Output "All done..." diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aci/README.md b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aci/README.md new file mode 100644 index 0000000000..cf91d3d055 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aci/README.md @@ -0,0 +1,23 @@ +# Linux GitHub Runner Docker Image for Azure Container Instances + +> Note: You can update the [Dockerfile](Dockerfile) to add any software that your require into the Azure DevOps agent, if you don't want to have to download the bits during all pipelines executions. + +## Environment Variables + +`GH_RUNNER_TOKEN`: The token used to authenticate the runner with GitHub. This can be a PAT or a self-hosted runner token. If supplying a self-hosted runner token, be aware that the token will expire after a few hours, so will only work with persistent runners. +`GH_RUNNER_URL`: The URL of the GitHub repository or organization (e.g. https://github.com/my-org or https://github.com/my-org/my-repo). +`GH_RUNNER_NAME`: The name of the runner as it appears in GitHub. +`GH_RUNNER_GROUP`: Optional. If not supplied, the runner will be added to the default group. This requires Enterprise licening. +`GH_RUNNER_MODE`: Supported values are `ephemeral` and `persistent`. Default is `ephemeral` if the env var is not supplied. + +## Build it + +```bash +docker build -t YOUR_IMAGE_NAME:YOUR_IMAGE_TAG . +``` + +## Push it + +```bash +docker push YOUR_IMAGE_NAME:YOUR_IMAGE_TAG +``` diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aci/dockerfile b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aci/dockerfile new file mode 100644 index 0000000000..a711634e05 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/github-runner-aci/dockerfile @@ -0,0 +1,35 @@ +FROM mcr.microsoft.com/azure-powershell:ubuntu-22.04 + +# To make it easier for build and release pipelines to run apt-get, +# configure apt to not require confirmation (assume the -y argument by default) +ENV DEBIAN_FRONTEND=noninteractive +RUN echo "APT::Get::Assume-Yes \"true\";" > /etc/apt/apt.conf.d/90assumeyes + +RUN apt-get update \ +&& apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + jq \ + git \ + iputils-ping \ + libcurl4 \ + libunwind8 \ + netcat \ + ruby \ + unzip \ + dnsutils \ + nodejs + +RUN curl -sL https://aka.ms/InstallAzureCLIDeb | bash + +WORKDIR /actions-runner + +COPY InstallAgent.ps1 InstallAgent.ps1 + +RUN ["pwsh", "InstallAgent.ps1"] + +COPY ConfigureAndRun.ps1 ConfigureAndRun.ps1 + +ENTRYPOINT ["pwsh"] + +CMD ["ConfigureAndRun.ps1"] \ No newline at end of file diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/startAzureDevOpsContainerJob.ps1 b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/startAzureDevOpsContainerJob.ps1 new file mode 100644 index 0000000000..e59e1b4794 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/scripts/startAzureDevOpsContainerJob.ps1 @@ -0,0 +1,36 @@ +<# +This scripts starts an Azure DevOps container job with a manual trigger. It is used to manually start the placeholder agent. +More Information can be found here https://learn.microsoft.com/azure/container-apps/tutorial-ci-cd-runners-jobs?tabs=bash&pivots=container-apps-jobs-self-hosted-ci-cd-azure-pipelines#create-a-placeholder-self-hosted-agent +#> + +Param( + [string]$jobName, + [string]$resourceGroup, + [string]$subscriptionId +) +$ErrorActionPreference = 'SilentlyContinue' +$DeploymentScriptOutputs = @{} +$maxRetries = 10 +$retryCount = 0 + +if (Get-AzContainerAppJob -Name $jobName -ResourceGroupName $resourceGroup -SubscriptionId $subscriptionId -ErrorVariable jobStartError) { + do { + $jobStatus = (Get-AzContainerAppJob -Name $jobName -ResourceGroupName $resourceGroup -SubscriptionId $subscriptionId).ProvisioningState + Write-Host 'Waiting for the container app job to start...Waiting 10 seconds!' + Start-Sleep -Seconds 10 + $retryCount++ + } until ($jobStatus -eq 'Succeeded' -or $maxRetries -eq $retryCount) + + $jobStart = Start-AzContainerAppJob -Name $jobName -ResourceGroupName $resourceGroup -SubscriptionId $subscriptionId -ErrorVariable jobStartError + if ($jobStart) { + Write-Host 'Job started successfully.' + $DeploymentScriptOutputs['JobStatus'] = 'Success' + } else { + Write-Host "Failed to start Job. An error occurred: $($jobStartError[0].Exception.Message)" + $DeploymentScriptOutputs['JobStatus'] = 'Failed' + } +} else { + Write-Host "Job not found. $($jobStartError[0].Exception.Message)" + $DeploymentScriptOutputs['JobStatus'] = 'Failed' +} + diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/aca-aci/main.test.bicep b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/aca-aci/main.test.bicep new file mode 100644 index 0000000000..d731ec0bad --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/aca-aci/main.test.bicep @@ -0,0 +1,63 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults for Azure DevOps self-hosted agents using both Azure Container Instances and Azure Container Apps.' +metadata description = 'This instance deploys the module with the minimum set of required parameters for Azure DevOps self-hosted agents in Azure Container Instances and Azure Container Apps.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-devopsrunners-${serviceShort}-rg' + +#disable-next-line no-hardcoded-location // Due to quotas and capacity challenges, this region must be used in the AVM testing subscription +var enforcedLocation = 'eastus2' + +@description('Optional. The personal access token for the Azure DevOps organization.') +@secure() +param personalAccessToken string = newGuid() + +@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 = 'acaaci' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ================= +// General resources +// ================= + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: enforcedLocation +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}' + scope: resourceGroup + params: { + namingPrefix: namePrefix + location: enforcedLocation + computeTypes: [ + 'azure-container-instance' + 'azure-container-app' + ] + selfHostedConfig: { + agentsPoolName: 'agents-pool' + devOpsOrganization: 'azureDevOpsOrganization' + personalAccessToken: personalAccessToken + selfHostedType: 'azuredevops' + } + networkingConfiguration: { + addressSpace: '10.0.0.0/16' + networkType: 'createNew' + virtualNetworkName: 'vnet-aca' + } + privateNetworking: false + } +} diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/defaults.azuredevops.aci/main.test.bicep b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/defaults.azuredevops.aci/main.test.bicep new file mode 100644 index 0000000000..0c475be7b2 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/defaults.azuredevops.aci/main.test.bicep @@ -0,0 +1,62 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults for Azure DevOps self-hosted agents using Azure Container Instances.' +metadata description = 'This instance deploys the module with the minimum set of required parameters for Azure DevOps self-hosted agents in Azure Container Instances.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-devopsrunners-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. The personal access token for the Azure DevOps organization.') +@secure() +param personalAccessToken string = newGuid() + +@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 = 'devaci' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ================= +// General resources +// ================= + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + scope: resourceGroup + params: { + namingPrefix: namePrefix + location: resourceLocation + computeTypes: [ + 'azure-container-instance' + ] + selfHostedConfig: { + agentsPoolName: 'aci-pool' + devOpsOrganization: 'azureDevOpsOrganization' + personalAccessToken: personalAccessToken + selfHostedType: 'azuredevops' + } + networkingConfiguration: { + addressSpace: '10.0.0.0/16' + networkType: 'createNew' + virtualNetworkName: 'vnet-aci' + } + privateNetworking: false + } +} diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/defaults.github.aca/main.test.bicep b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/defaults.github.aca/main.test.bicep new file mode 100644 index 0000000000..0982d6a8cf --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/defaults.github.aca/main.test.bicep @@ -0,0 +1,61 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults for GitHub self-hosted runners using Azure Container Apps.' +metadata description = 'This instance deploys the module with the minimum set of required parameters for GitHub self-hosted runners in Azure Container Apps.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-githubRunner-${serviceShort}-rg' + +#disable-next-line no-hardcoded-location // Due to quotas and capacity challenges, this region must be used in the AVM testing subscription +var enforcedLocation = 'eastus2' + +@description('Optional. The personal access token for the GitHub organization.') +@secure() +param personalAccessToken string = newGuid() + +@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 = 'ghaca' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ================= +// General resources +// ================= + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: enforcedLocation +} + +// ============== // +// Test Execution // +// ============== // +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}' + scope: resourceGroup + params: { + namingPrefix: namePrefix + location: enforcedLocation + computeTypes: [ + 'azure-container-app' + ] + selfHostedConfig: { + githubOrganization: 'githHubOrganization' + githubRepository: 'dummyRepo' + personalAccessToken: personalAccessToken + selfHostedType: 'github' + } + networkingConfiguration: { + addressSpace: '10.0.0.0/16' + networkType: 'createNew' + virtualNetworkName: 'vnet-aca' + } + privateNetworking: false + } +} diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/max.azuredevops.aca/main.test.bicep b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/max.azuredevops.aca/main.test.bicep new file mode 100644 index 0000000000..9d52645d7e --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/max.azuredevops.aca/main.test.bicep @@ -0,0 +1,73 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set for Azure DevOps self-hosted agents using Azure Container Apps.' +metadata description = 'This instance deploys the module with most of its features enabled for Azure DevOps self-hosted agents using Azure Container Apps.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-devopsrunners-${serviceShort}-rg' + +#disable-next-line no-hardcoded-location // Due to quotas and capacity challenges, this region must be used in the AVM testing subscription +var enforcedLocation = 'eastus2' + +@description('Optional. The personal access token for the Azure DevOps organization.') +@secure() +param personalAccessToken string = newGuid() + +@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 = 'mxdev' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ================= +// General resources +// ================= + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: enforcedLocation +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}' + scope: resourceGroup + params: { + namingPrefix: namePrefix + location: enforcedLocation + computeTypes: [ + 'azure-container-app' + ] + selfHostedConfig: { + agentsPoolName: 'aca-pool' + devOpsOrganization: 'azureDevOpsOrganization' + personalAccessToken: personalAccessToken + agentNamePrefix: namePrefix + azureContainerAppTarget: { + resources: { + cpu: '1' + memory: '2Gi' + } + } + placeHolderAgentName: 'acaPlaceHolderAgent' + targetPipelinesQueueLength: '1' + selfHostedType: 'azuredevops' + } + networkingConfiguration: { + virtualNetworkName: 'vnet-aca' + addressSpace: '10.0.0.0/16' + networkType: 'createNew' + containerAppSubnetName: 'acaSubnet' + containerAppSubnetAddressPrefix: '10.0.1.0/24' + } + privateNetworking: false + } +} diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/max.github.aci/main.test.bicep b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/max.github.aci/main.test.bicep new file mode 100644 index 0000000000..d5785fbcc5 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/max.github.aci/main.test.bicep @@ -0,0 +1,74 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set for GitHub self-hosted runners using Azure Container Instances.' +metadata description = 'This instance deploys the module with most of its features enabled for GitHub self-hosted runners using Azure Container Instances.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-githubRunner-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. The personal access token for the GitHub organization.') +@secure() +param personalAccessToken string = newGuid() + +@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 = 'mxgh' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ================= +// General resources +// ================= + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + scope: resourceGroup + params: { + namingPrefix: namePrefix + location: resourceLocation + computeTypes: [ + 'azure-container-instance' + ] + selfHostedConfig: { + githubOrganization: 'githHubOrganization' + githubRepository: 'dummyRepo' + personalAccessToken: personalAccessToken + ephemeral: true + runnerNamePrefix: namePrefix + runnerScope: 'repo' + targetWorkflowQueueLength: '1' + azureContainerInstanceTarget: { + sku: 'Standard' + cpu: 1 + memoryInGB: 2 + numberOfInstances: 3 + } + selfHostedType: 'github' + } + networkingConfiguration: { + addressSpace: '10.0.0.0/16' + networkType: 'createNew' + virtualNetworkName: 'vnet-aci' + containerInstanceSubnetName: 'aci-subnet' + containerInstanceSubnetAddressPrefix: '10.0.1.0/24' + } + privateNetworking: false + } +} diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-devops-existing-vnet-aci/dependencies.bicep b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-devops-existing-vnet-aci/dependencies.bicep new file mode 100644 index 0000000000..8544d7f228 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-devops-existing-vnet-aci/dependencies.bicep @@ -0,0 +1,105 @@ +@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 address to create.') +param publicIPName string + +@description('Required. The name of the NAT gateway to create.') +param natGatewayName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'acr-subnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 2) + } + } + { + name: 'aci-subnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 3) + delegations: [ + { + name: 'Microsoft.ContainerInstance/containerGroups' + properties: { + serviceName: 'Microsoft.ContainerInstance/containerGroups' + } + } + ] + natGateway: { + id: natGateway.id + } + } + } + ] + } +} + +resource natGateway 'Microsoft.Network/natGateways@2024-01-01' = { + name: natGatewayName + location: location + sku: { + name: 'Standard' + } + properties: { + publicIpAddresses: [ + { + id: publicIP.id + } + ] + } +} + +resource acrPrivateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.azurecr.io' + location: 'global' +} + +resource vnetLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { + name: '${acrPrivateDNSZone.name}-link' + location: 'global' + parent: acrPrivateDNSZone + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } +} + +resource publicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { + name: publicIPName + location: location + sku: { + name: 'Standard' + } + properties: { + publicIPAllocationMethod: 'Static' + } +} + +@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 resource ID of the created NAT gateway.') +output natGatewayResourceId string = natGateway.id + +@description('The resource ID of the created private DNS zone.') +output acrPrivateDNSZoneResourceId string = acrPrivateDNSZone.id diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-devops-existing-vnet-aci/main.test.bicep b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-devops-existing-vnet-aci/main.test.bicep new file mode 100644 index 0000000000..1872a81219 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-devops-existing-vnet-aci/main.test.bicep @@ -0,0 +1,87 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults for Azure DevOps self-hosted agents using Private networking in an existing vnet.' +metadata description = 'This instance deploys the module with the minimum set of required parameters Azure DevOps self-hosted agents using Private networking in Azure Container Instances in an existing vnet.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-azuredevops-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. The personal access token for the Azure DevOps organization.') +@secure() +param personalAccessToken string = newGuid() + +@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 = 'pnexa' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ================= +// General resources +// ================= + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ================= +// Dependencies +// ================= +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + publicIPName: 'dep-${namePrefix}-pip-${serviceShort}' + natGatewayName: 'dep-${namePrefix}-natGw-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + scope: resourceGroup + params: { + namingPrefix: namePrefix + location: resourceLocation + computeTypes: [ + 'azure-container-instance' + ] + selfHostedConfig: { + selfHostedType: 'azuredevops' + agentsPoolName: 'aci-pool' + devOpsOrganization: 'azureDevOpsOrganization' + personalAccessToken: personalAccessToken + agentNamePrefix: namePrefix + azureContainerInstanceTarget: { + numberOfInstances: 2 + } + } + networkingConfiguration: { + networkType: 'useExisting' + virtualNetworkResourceId: nestedDependencies.outputs.virtualNetworkResourceId + containerRegistryPrivateEndpointSubnetName: 'acr-subnet' + containerRegistryPrivateDnsZoneResourceId: nestedDependencies.outputs.acrPrivateDNSZoneResourceId + natGatewayPublicIpAddressResourceId: nestedDependencies.outputs.publicIPResourceId + natGatewayResourceId: nestedDependencies.outputs.natGatewayResourceId + computeNetworking: { + computeNetworkType: 'azureContainerInstance' + containerInstanceSubnetName: 'aci-subnet' + } + } + privateNetworking: true + } +} diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-github-existing-vnet-aca/dependencies.bicep b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-github-existing-vnet-aca/dependencies.bicep new file mode 100644 index 0000000000..1e8f6949e5 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-github-existing-vnet-aca/dependencies.bicep @@ -0,0 +1,145 @@ +@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 address to create.') +param publicIPName string + +@description('Required. The name of the NAT gateway to create.') +param natGatewayName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'aca-subnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 0) + natGateway: { + id: natGateway.id + } + delegations: [ + { + name: 'Microsoft.App.environments' + properties: { + serviceName: 'Microsoft.App/environments' + } + } + ] + } + } + { + name: 'aca-ds-subnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 1) + } + } + { + name: 'acr-subnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 2) + } + } + { + name: 'aci-subnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 3) + delegations: [ + { + name: 'Microsoft.ContainerInstance/containerGroups' + properties: { + serviceName: 'Microsoft.ContainerInstance/containerGroups' + } + } + ] + } + } + ] + } +} + +resource natGateway 'Microsoft.Network/natGateways@2024-01-01' = { + name: natGatewayName + location: location + sku: { + name: 'Standard' + } + properties: { + publicIpAddresses: [ + { + id: publicIP.id + } + ] + } +} + +resource acrPrivateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.azurecr.io' + location: 'global' +} + +resource deploymentScriptPrivateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink.file.${environment().suffixes.storage}' + location: 'global' +} + +resource acrVnetLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { + name: '${acrPrivateDNSZone.name}-link' + location: 'global' + parent: acrPrivateDNSZone + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } +} + +resource dsVnetLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = { + name: '${deploymentScriptPrivateDNSZone.name}-link' + location: 'global' + parent: deploymentScriptPrivateDNSZone + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } +} + +resource publicIP 'Microsoft.Network/publicIPAddresses@2023-04-01' = { + name: publicIPName + location: location + sku: { + name: 'Standard' + } + properties: { + publicIPAllocationMethod: 'Static' + } +} + +@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 resource ID of the created NAT gateway.') +output natGatewayResourceId string = natGateway.id + +@description('The resource ID of the created ACR private DNS zone.') +output acrPrivateDNSZoneId string = acrPrivateDNSZone.id + +@description('The resource ID of the created deployment script private DNS zone.') +output deploymentScriptPrivateDnsZoneResourceId string = deploymentScriptPrivateDNSZone.id diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-github-existing-vnet-aca/main.test.bicep b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-github-existing-vnet-aca/main.test.bicep new file mode 100644 index 0000000000..87bf8e6ea4 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-github-existing-vnet-aca/main.test.bicep @@ -0,0 +1,86 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults for GitHub self-hosted runners using Private networking in an existing vnet.' +metadata description = 'This instance deploys the module with the minimum set of required parameters GitHub self-hosted runners using Private networking in Azure Container Apps in an existing vnet.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-githubrunners-${serviceShort}-rg' + +#disable-next-line no-hardcoded-location // Due to quotas and capacity challenges, this region must be used in the AVM testing subscription +var enforcedLocation = 'eastus2' + +@description('Optional. The personal access token for the Azure DevOps organization.') +@secure() +param personalAccessToken string = newGuid() + +@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 = 'pnexg' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ================= +// General resources +// ================= + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: enforcedLocation +} + +// ================= +// Dependencies +// ================= +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' + params: { + location: enforcedLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + publicIPName: 'dep-${namePrefix}-pip-${serviceShort}' + natGatewayName: 'dep-${namePrefix}-natGw-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}' + scope: resourceGroup + params: { + namingPrefix: namePrefix + location: enforcedLocation + computeTypes: [ + 'azure-container-instance' + ] + selfHostedConfig: { + githubOrganization: 'githHubOrganization' + githubRepository: 'dummyRepo' + personalAccessToken: personalAccessToken + selfHostedType: 'github' + } + networkingConfiguration: { + networkType: 'useExisting' + virtualNetworkResourceId: nestedDependencies.outputs.virtualNetworkResourceId + containerRegistryPrivateDnsZoneResourceId: nestedDependencies.outputs.acrPrivateDNSZoneId + containerRegistryPrivateEndpointSubnetName: 'acr-subnet' + natGatewayPublicIpAddressResourceId: nestedDependencies.outputs.publicIPResourceId + natGatewayResourceId: nestedDependencies.outputs.natGatewayResourceId + computeNetworking: { + containerAppDeploymentScriptSubnetName: 'aca-ds-subnet' + containerAppSubnetName: 'aca-subnet' + deploymentScriptPrivateDnsZoneResourceId: nestedDependencies.outputs.deploymentScriptPrivateDnsZoneResourceId + containerInstanceSubnetName: 'aci-subnet' + computeNetworkType: 'azureContainerApp' + } + } + privateNetworking: true + } +} diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-github-new-vnet/main.test.bicep b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-github-new-vnet/main.test.bicep new file mode 100644 index 0000000000..08fa9389b3 --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/tests/e2e/private-github-new-vnet/main.test.bicep @@ -0,0 +1,62 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults for GitHub self-hosted runners using Private networking.' +metadata description = 'This instance deploys the module with the minimum set of required parameters GitHub self-hosted runners using Private networking in Azure Container Instances.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-githubrunners-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. The personal access token for the Azure DevOps organization.') +@secure() +param personalAccessToken string = newGuid() + +@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 = 'pngh' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ================= +// General resources +// ================= + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + scope: resourceGroup + params: { + namingPrefix: namePrefix + location: resourceLocation + computeTypes: [ + 'azure-container-instance' + ] + selfHostedConfig: { + githubOrganization: 'githHubOrganization' + githubRepository: 'dummyRepo' + personalAccessToken: personalAccessToken + selfHostedType: 'github' + } + networkingConfiguration: { + addressSpace: '10.0.0.0/16' + networkType: 'createNew' + virtualNetworkName: 'vnet-aci' + } + privateNetworking: true + } +} diff --git a/avm/ptn/dev-ops/cicd-agents-and-runners/version.json b/avm/ptn/dev-ops/cicd-agents-and-runners/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/ptn/dev-ops/cicd-agents-and-runners/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From 969ebd6b599eedf5d39c62d6d85db8aa0a035b94 Mon Sep 17 00:00:00 2001 From: Kris Baranek <20225789+krbar@users.noreply.github.com> Date: Sun, 8 Sep 2024 13:06:04 +0200 Subject: [PATCH 03/57] fix: Remove '(Preview)' label from role name - batch 3 (#3223) ## Description Remove '(Preview)' label from all 'Role Based Access Control Administrator' role as described in #3112 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.db-for-postgre-sql.flexible-server](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.db-for-postgre-sql.flexible-server.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.db-for-postgre-sql.flexible-server.yml) | | [![avm.res.desktop-virtualization.application-group](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.application-group.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.application-group.yml) | | [![avm.res.desktop-virtualization.host-pool](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.host-pool.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.host-pool.yml) | | [![avm.res.desktop-virtualization.scaling-plan](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.scaling-plan.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.scaling-plan.yml) | | [![avm.res.desktop-virtualization.workspace](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.workspace.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.workspace.yml) | | [![avm.res.event-grid.domain](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.event-grid.domain.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.event-grid.domain.yml) | | [![avm.res.event-grid.system-topic](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.event-grid.system-topic.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.event-grid.system-topic.yml) | | [![avm.res.event-grid.topic](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.event-grid.topic.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.event-grid.topic.yml) | | [![avm.res.event-hub.namespace](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.event-hub.namespace.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.event-hub.namespace.yml) | | [![avm.res.health-bot.health-bot](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.health-bot.health-bot.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.health-bot.health-bot.yml) | | [![avm.res.healthcare-apis.workspace](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.healthcare-apis.workspace.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.healthcare-apis.workspace.yml) | | [![avm.res.hybrid-compute.machine](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.hybrid-compute.machine.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.hybrid-compute.machine.yml) | | [![avm.res.insights.action-group](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.insights.action-group.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.insights.action-group.yml) | | [![avm.res.insights.activity-log-alert](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.insights.activity-log-alert.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.insights.activity-log-alert.yml) | | [![avm.res.insights.data-collection-endpoint](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.insights.data-collection-endpoint.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.insights.data-collection-endpoint.yml) | | [![avm.res.insights.metric-alert](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.insights.metric-alert.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.insights.metric-alert.yml) | | [![avm.res.insights.scheduled-query-rule](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.insights.scheduled-query-rule.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.insights.scheduled-query-rule.yml) | | [![avm.res.insights.webtest](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.insights.webtest.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.insights.webtest.yml) | | [![avm.res.key-vault.vault](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml) | | [![avm.res.load-test-service.load-test](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.load-test-service.load-test.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.load-test-service.load-test.yml) | | [![avm.res.logic.workflow](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.logic.workflow.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.logic.workflow.yml) | | [![avm.res.machine-learning-services.workspace](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.machine-learning-services.workspace.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.machine-learning-services.workspace.yml) | | [![avm.res.maintenance.maintenance-configuration](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.maintenance.maintenance-configuration.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.maintenance.maintenance-configuration.yml) | | [![avm.res.managed-identity.user-assigned-identity](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.managed-identity.user-assigned-identity.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.managed-identity.user-assigned-identity.yml) | | [![avm.res.net-app.net-app-account](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.net-app.net-app-account.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename2&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.net-app.net-app-account.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [x] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../db-for-postgre-sql/flexible-server/main.bicep | 2 +- avm/res/db-for-postgre-sql/flexible-server/main.json | 4 ++-- .../db-for-postgre-sql/flexible-server/version.json | 2 +- .../application-group/main.bicep | 2 +- .../application-group/main.json | 4 ++-- .../application-group/version.json | 2 +- avm/res/desktop-virtualization/host-pool/main.bicep | 2 +- avm/res/desktop-virtualization/host-pool/main.json | 4 ++-- .../desktop-virtualization/host-pool/version.json | 2 +- .../desktop-virtualization/scaling-plan/main.bicep | 2 +- .../desktop-virtualization/scaling-plan/main.json | 4 ++-- .../desktop-virtualization/scaling-plan/version.json | 2 +- avm/res/desktop-virtualization/workspace/main.bicep | 2 +- avm/res/desktop-virtualization/workspace/main.json | 4 ++-- .../desktop-virtualization/workspace/version.json | 2 +- avm/res/event-grid/domain/main.bicep | 2 +- avm/res/event-grid/domain/main.json | 4 ++-- avm/res/event-grid/domain/version.json | 2 +- avm/res/event-grid/system-topic/main.bicep | 2 +- avm/res/event-grid/system-topic/main.json | 4 ++-- avm/res/event-grid/system-topic/version.json | 2 +- avm/res/event-grid/topic/main.bicep | 2 +- avm/res/event-grid/topic/main.json | 4 ++-- avm/res/event-grid/topic/version.json | 2 +- avm/res/event-hub/namespace/eventhub/main.bicep | 2 +- avm/res/event-hub/namespace/eventhub/main.json | 4 ++-- avm/res/event-hub/namespace/main.bicep | 2 +- avm/res/event-hub/namespace/main.json | 8 ++++---- avm/res/event-hub/namespace/version.json | 2 +- avm/res/health-bot/health-bot/main.bicep | 2 +- avm/res/health-bot/health-bot/main.json | 4 ++-- avm/res/health-bot/health-bot/version.json | 2 +- .../healthcare-apis/workspace/fhirservice/main.bicep | 2 +- .../healthcare-apis/workspace/fhirservice/main.json | 4 ++-- avm/res/healthcare-apis/workspace/main.bicep | 2 +- avm/res/healthcare-apis/workspace/main.json | 8 ++++---- avm/res/healthcare-apis/workspace/version.json | 2 +- avm/res/hybrid-compute/machine/main.bicep | 2 +- avm/res/hybrid-compute/machine/main.json | 4 ++-- avm/res/hybrid-compute/machine/version.json | 2 +- avm/res/insights/action-group/main.bicep | 2 +- avm/res/insights/action-group/main.json | 4 ++-- avm/res/insights/action-group/version.json | 2 +- avm/res/insights/activity-log-alert/main.bicep | 2 +- avm/res/insights/activity-log-alert/main.json | 4 ++-- avm/res/insights/activity-log-alert/version.json | 2 +- avm/res/insights/data-collection-endpoint/main.bicep | 2 +- avm/res/insights/data-collection-endpoint/main.json | 4 ++-- .../insights/data-collection-endpoint/version.json | 2 +- avm/res/insights/metric-alert/main.bicep | 2 +- avm/res/insights/metric-alert/main.json | 4 ++-- avm/res/insights/metric-alert/version.json | 4 ++-- avm/res/insights/scheduled-query-rule/main.bicep | 2 +- avm/res/insights/scheduled-query-rule/main.json | 4 ++-- avm/res/insights/scheduled-query-rule/version.json | 2 +- avm/res/insights/webtest/main.bicep | 2 +- avm/res/insights/webtest/main.json | 6 +++--- avm/res/insights/webtest/version.json | 2 +- avm/res/key-vault/vault/key/main.bicep | 2 +- avm/res/key-vault/vault/key/main.json | 4 ++-- avm/res/key-vault/vault/main.bicep | 2 +- avm/res/key-vault/vault/main.json | 12 ++++++------ avm/res/key-vault/vault/secret/main.bicep | 2 +- avm/res/key-vault/vault/secret/main.json | 4 ++-- avm/res/key-vault/vault/version.json | 2 +- avm/res/load-test-service/load-test/main.bicep | 2 +- avm/res/load-test-service/load-test/main.json | 4 ++-- avm/res/load-test-service/load-test/version.json | 2 +- avm/res/logic/workflow/main.bicep | 2 +- avm/res/logic/workflow/main.json | 4 ++-- avm/res/logic/workflow/version.json | 2 +- .../machine-learning-services/workspace/main.bicep | 2 +- .../machine-learning-services/workspace/main.json | 4 ++-- .../machine-learning-services/workspace/version.json | 4 ++-- .../maintenance/maintenance-configuration/main.bicep | 2 +- .../maintenance/maintenance-configuration/main.json | 4 ++-- .../maintenance-configuration/version.json | 2 +- .../user-assigned-identity/main.bicep | 2 +- .../user-assigned-identity/main.json | 4 ++-- .../user-assigned-identity/version.json | 2 +- .../net-app/net-app-account/capacity-pool/main.bicep | 2 +- .../net-app/net-app-account/capacity-pool/main.json | 8 ++++---- .../net-app-account/capacity-pool/volume/main.bicep | 2 +- .../net-app-account/capacity-pool/volume/main.json | 4 ++-- avm/res/net-app/net-app-account/main.bicep | 2 +- avm/res/net-app/net-app-account/main.json | 12 ++++++------ avm/res/net-app/net-app-account/version.json | 2 +- 87 files changed, 135 insertions(+), 135 deletions(-) diff --git a/avm/res/db-for-postgre-sql/flexible-server/main.bicep b/avm/res/db-for-postgre-sql/flexible-server/main.bicep index 24a84a31e8..19c07e2e2f 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/main.bicep +++ b/avm/res/db-for-postgre-sql/flexible-server/main.bicep @@ -178,7 +178,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/db-for-postgre-sql/flexible-server/main.json b/avm/res/db-for-postgre-sql/flexible-server/main.json index 2c67a4f617..9472f5d8f9 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/main.json +++ b/avm/res/db-for-postgre-sql/flexible-server/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4308585226053074434" + "templateHash": "745168326315156090" }, "name": "DBforPostgreSQL Flexible Servers", "description": "This module deploys a DBforPostgreSQL Flexible Server.", @@ -571,7 +571,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/db-for-postgre-sql/flexible-server/version.json b/avm/res/db-for-postgre-sql/flexible-server/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/version.json +++ b/avm/res/db-for-postgre-sql/flexible-server/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/desktop-virtualization/application-group/main.bicep b/avm/res/desktop-virtualization/application-group/main.bicep index 7a54d23c4f..e6ae1d5da9 100644 --- a/avm/res/desktop-virtualization/application-group/main.bicep +++ b/avm/res/desktop-virtualization/application-group/main.bicep @@ -47,7 +47,7 @@ var builtInRoleNames = { Owner: '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635' Contributor: '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' Reader: '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7' - 'Role Based Access Control Administrator (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168' + 'Role Based Access Control Administrator': '/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168' 'User Access Administrator': '/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' 'Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b' 'Desktop Virtualization Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8' diff --git a/avm/res/desktop-virtualization/application-group/main.json b/avm/res/desktop-virtualization/application-group/main.json index be13b3e18a..f626bd560d 100644 --- a/avm/res/desktop-virtualization/application-group/main.json +++ b/avm/res/desktop-virtualization/application-group/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "10367024700295451803" + "templateHash": "14277084174605049439" }, "name": "Azure Virtual Desktop Application Group", "description": "This module deploys an Azure Virtual Desktop Application Group.", @@ -304,7 +304,7 @@ "Owner": "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635", "Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c", "Reader": "/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7", - "Role Based Access Control Administrator (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168", + "Role Based Access Control Administrator": "/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168", "User Access Administrator": "/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9", "Application Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b", "Desktop Virtualization Application Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8", diff --git a/avm/res/desktop-virtualization/application-group/version.json b/avm/res/desktop-virtualization/application-group/version.json index 729ac87673..76049e1c4a 100644 --- a/avm/res/desktop-virtualization/application-group/version.json +++ b/avm/res/desktop-virtualization/application-group/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/desktop-virtualization/host-pool/main.bicep b/avm/res/desktop-virtualization/host-pool/main.bicep index b3493783a3..2084a17dd9 100644 --- a/avm/res/desktop-virtualization/host-pool/main.bicep +++ b/avm/res/desktop-virtualization/host-pool/main.bicep @@ -133,7 +133,7 @@ var builtInRoleNames = { Owner: '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635' Contributor: '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' Reader: '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7' - 'Role Based Access Control Administrator (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168' + 'Role Based Access Control Administrator': '/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168' 'User Access Administrator': '/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' 'Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b' 'Desktop Virtualization Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8' diff --git a/avm/res/desktop-virtualization/host-pool/main.json b/avm/res/desktop-virtualization/host-pool/main.json index dcf53b63e3..d11ccd0220 100644 --- a/avm/res/desktop-virtualization/host-pool/main.json +++ b/avm/res/desktop-virtualization/host-pool/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16196020980730321082" + "templateHash": "988183823258334261" }, "name": "Azure Virtual Desktop Host Pool", "description": "This module deploys an Azure Virtual Desktop Host Pool", @@ -673,7 +673,7 @@ "Owner": "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635", "Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c", "Reader": "/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7", - "Role Based Access Control Administrator (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168", + "Role Based Access Control Administrator": "/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168", "User Access Administrator": "/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9", "Application Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b", "Desktop Virtualization Application Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8", diff --git a/avm/res/desktop-virtualization/host-pool/version.json b/avm/res/desktop-virtualization/host-pool/version.json index 13669e6601..ea4f3b6e67 100644 --- a/avm/res/desktop-virtualization/host-pool/version.json +++ b/avm/res/desktop-virtualization/host-pool/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/desktop-virtualization/scaling-plan/main.bicep b/avm/res/desktop-virtualization/scaling-plan/main.bicep index 15ad9a9d17..aa3a78bc05 100644 --- a/avm/res/desktop-virtualization/scaling-plan/main.bicep +++ b/avm/res/desktop-virtualization/scaling-plan/main.bicep @@ -52,7 +52,7 @@ var builtInRoleNames = { Owner: '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635' Contributor: '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' Reader: '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7' - 'Role Based Access Control Administrator (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168' + 'Role Based Access Control Administrator': '/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168' 'User Access Administrator': '/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' 'Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b' 'Desktop Virtualization Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8' diff --git a/avm/res/desktop-virtualization/scaling-plan/main.json b/avm/res/desktop-virtualization/scaling-plan/main.json index 1c4786cdbe..e32a489cc6 100644 --- a/avm/res/desktop-virtualization/scaling-plan/main.json +++ b/avm/res/desktop-virtualization/scaling-plan/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "7189430344651214468" + "templateHash": "10729505657035583353" }, "name": "Azure Virtual Desktop Scaling Plan", "description": "This module deploys an Azure Virtual Desktop Scaling Plan.", @@ -319,7 +319,7 @@ "Owner": "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635", "Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c", "Reader": "/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7", - "Role Based Access Control Administrator (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168", + "Role Based Access Control Administrator": "/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168", "User Access Administrator": "/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9", "Application Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b", "Desktop Virtualization Application Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8", diff --git a/avm/res/desktop-virtualization/scaling-plan/version.json b/avm/res/desktop-virtualization/scaling-plan/version.json index 729ac87673..76049e1c4a 100644 --- a/avm/res/desktop-virtualization/scaling-plan/version.json +++ b/avm/res/desktop-virtualization/scaling-plan/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/desktop-virtualization/workspace/main.bicep b/avm/res/desktop-virtualization/workspace/main.bicep index c9a61e0606..b491177c31 100644 --- a/avm/res/desktop-virtualization/workspace/main.bicep +++ b/avm/res/desktop-virtualization/workspace/main.bicep @@ -42,7 +42,7 @@ var builtInRoleNames = { Owner: '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635' Contributor: '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c' Reader: '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7' - 'Role Based Access Control Administrator (Preview)': '/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168' + 'Role Based Access Control Administrator': '/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168' 'User Access Administrator': '/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' 'Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b' 'Desktop Virtualization Application Group Contributor': '/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8' diff --git a/avm/res/desktop-virtualization/workspace/main.json b/avm/res/desktop-virtualization/workspace/main.json index 2197a9e47d..de5bdd06a4 100644 --- a/avm/res/desktop-virtualization/workspace/main.json +++ b/avm/res/desktop-virtualization/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11955333663184488007" + "templateHash": "8425031488997615382" }, "name": "Workspace", "description": "This module deploys an Azure Virtual Desktop Workspace.", @@ -513,7 +513,7 @@ "Owner": "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635", "Contributor": "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c", "Reader": "/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7", - "Role Based Access Control Administrator (Preview)": "/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168", + "Role Based Access Control Administrator": "/providers/Microsoft.Authorization/roleDefinitions/f58310d9-a9f6-439a-9e8d-f62e7b41a168", "User Access Administrator": "/providers/Microsoft.Authorization/roleDefinitions/18d7d88d-d35e-4fb5-a5c3-7773c20a72d9", "Application Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/ca6382a4-1721-4bcf-a114-ff0c70227b6b", "Desktop Virtualization Application Group Contributor": "/providers/Microsoft.Authorization/roleDefinitions/86240b0e-9422-4c43-887b-b61143f32ba8", diff --git a/avm/res/desktop-virtualization/workspace/version.json b/avm/res/desktop-virtualization/workspace/version.json index 21226dd43f..09c3664cec 100644 --- a/avm/res/desktop-virtualization/workspace/version.json +++ b/avm/res/desktop-virtualization/workspace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.6", + "version": "0.7", "pathFilters": [ "./main.json" ] diff --git a/avm/res/event-grid/domain/main.bicep b/avm/res/event-grid/domain/main.bicep index 5c6faac2cc..ddd2595ac3 100644 --- a/avm/res/event-grid/domain/main.bicep +++ b/avm/res/event-grid/domain/main.bicep @@ -87,7 +87,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/event-grid/domain/main.json b/avm/res/event-grid/domain/main.json index 3c794e8617..f53f2e65df 100644 --- a/avm/res/event-grid/domain/main.json +++ b/avm/res/event-grid/domain/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "15360544071637596011" + "templateHash": "5221715648004087997" }, "name": "Event Grid Domains", "description": "This module deploys an Event Grid Domain.", @@ -599,7 +599,7 @@ "EventGrid EventSubscription Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2414bbcf-6497-4faf-8c65-045460748405')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/event-grid/domain/version.json b/avm/res/event-grid/domain/version.json index ea4f3b6e67..21226dd43f 100644 --- a/avm/res/event-grid/domain/version.json +++ b/avm/res/event-grid/domain/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] diff --git a/avm/res/event-grid/system-topic/main.bicep b/avm/res/event-grid/system-topic/main.bicep index 9673756db5..fc307fc99c 100644 --- a/avm/res/event-grid/system-topic/main.bicep +++ b/avm/res/event-grid/system-topic/main.bicep @@ -70,7 +70,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/event-grid/system-topic/main.json b/avm/res/event-grid/system-topic/main.json index 36a34561c0..553b7c21ad 100644 --- a/avm/res/event-grid/system-topic/main.json +++ b/avm/res/event-grid/system-topic/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "18332319378144663212" + "templateHash": "16862867732886846778" }, "name": "Event Grid System Topics", "description": "This module deploys an Event Grid System Topic.", @@ -345,7 +345,7 @@ "EventGrid EventSubscription Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2414bbcf-6497-4faf-8c65-045460748405')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/event-grid/system-topic/version.json b/avm/res/event-grid/system-topic/version.json index 76049e1c4a..13669e6601 100644 --- a/avm/res/event-grid/system-topic/version.json +++ b/avm/res/event-grid/system-topic/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/event-grid/topic/main.bicep b/avm/res/event-grid/topic/main.bicep index 400d3c498b..9b0a660f01 100644 --- a/avm/res/event-grid/topic/main.bicep +++ b/avm/res/event-grid/topic/main.bicep @@ -81,7 +81,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/event-grid/topic/main.json b/avm/res/event-grid/topic/main.json index 8e1ca6e46e..a0b065e629 100644 --- a/avm/res/event-grid/topic/main.json +++ b/avm/res/event-grid/topic/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "14857992424143462934" + "templateHash": "12405995366615613618" }, "name": "Event Grid Topics", "description": "This module deploys an Event Grid Topic.", @@ -585,7 +585,7 @@ "EventGrid EventSubscription Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2414bbcf-6497-4faf-8c65-045460748405')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/event-grid/topic/version.json b/avm/res/event-grid/topic/version.json index ea4f3b6e67..21226dd43f 100644 --- a/avm/res/event-grid/topic/version.json +++ b/avm/res/event-grid/topic/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] diff --git a/avm/res/event-hub/namespace/eventhub/main.bicep b/avm/res/event-hub/namespace/eventhub/main.bicep index e3c6fe3091..df3b0c6a29 100644 --- a/avm/res/event-hub/namespace/eventhub/main.bicep +++ b/avm/res/event-hub/namespace/eventhub/main.bicep @@ -158,7 +158,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/event-hub/namespace/eventhub/main.json b/avm/res/event-hub/namespace/eventhub/main.json index c3910acbe4..4ca5b57584 100644 --- a/avm/res/event-hub/namespace/eventhub/main.json +++ b/avm/res/event-hub/namespace/eventhub/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16769928083256444746" + "templateHash": "11002403097253998911" }, "name": "Event Hub Namespace Event Hubs", "description": "This module deploys an Event Hub Namespace Event Hub.", @@ -343,7 +343,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/event-hub/namespace/main.bicep b/avm/res/event-hub/namespace/main.bicep index bc225dd8af..9f203c5398 100644 --- a/avm/res/event-hub/namespace/main.bicep +++ b/avm/res/event-hub/namespace/main.bicep @@ -137,7 +137,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/event-hub/namespace/main.json b/avm/res/event-hub/namespace/main.json index 7856e8e980..e16a166f74 100644 --- a/avm/res/event-hub/namespace/main.json +++ b/avm/res/event-hub/namespace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "3566282798641159484" + "templateHash": "15332291729708806177" }, "name": "Event Hub Namespaces", "description": "This module deploys an Event Hub Namespace.", @@ -718,7 +718,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -1119,7 +1119,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16769928083256444746" + "templateHash": "11002403097253998911" }, "name": "Event Hub Namespace Event Hubs", "description": "This module deploys an Event Hub Namespace Event Hub.", @@ -1456,7 +1456,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/event-hub/namespace/version.json b/avm/res/event-hub/namespace/version.json index e42c3d9e5f..7e1d3f4157 100644 --- a/avm/res/event-hub/namespace/version.json +++ b/avm/res/event-hub/namespace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.6", + "version": "0.7", "pathFilters": [ "./main.json" ] diff --git a/avm/res/health-bot/health-bot/main.bicep b/avm/res/health-bot/health-bot/main.bicep index 73b357c7b9..5eadd66157 100644 --- a/avm/res/health-bot/health-bot/main.bicep +++ b/avm/res/health-bot/health-bot/main.bicep @@ -48,7 +48,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/health-bot/health-bot/main.json b/avm/res/health-bot/health-bot/main.json index cc92a85935..6ee924eb9a 100644 --- a/avm/res/health-bot/health-bot/main.json +++ b/avm/res/health-bot/health-bot/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "15586747651059655196" + "templateHash": "13204699140769654908" }, "name": "Azure Health Bots", "description": "This module deploys an Azure Health Bot.", @@ -199,7 +199,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/health-bot/health-bot/version.json b/avm/res/health-bot/health-bot/version.json index 729ac87673..76049e1c4a 100644 --- a/avm/res/health-bot/health-bot/version.json +++ b/avm/res/health-bot/health-bot/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/healthcare-apis/workspace/fhirservice/main.bicep b/avm/res/healthcare-apis/workspace/fhirservice/main.bicep index 2dde69b5f5..b9db3625db 100644 --- a/avm/res/healthcare-apis/workspace/fhirservice/main.bicep +++ b/avm/res/healthcare-apis/workspace/fhirservice/main.bicep @@ -168,7 +168,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/healthcare-apis/workspace/fhirservice/main.json b/avm/res/healthcare-apis/workspace/fhirservice/main.json index 97cb613498..eb0c2c801c 100644 --- a/avm/res/healthcare-apis/workspace/fhirservice/main.json +++ b/avm/res/healthcare-apis/workspace/fhirservice/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "1079296911965087473" + "templateHash": "17664239331720913904" }, "name": "Healthcare API Workspace FHIR Services", "description": "This module deploys a Healthcare API Workspace FHIR Service.", @@ -489,7 +489,7 @@ "FHIR SMART User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4ba50f17-9666-485c-a643-ff00808643f0')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/healthcare-apis/workspace/main.bicep b/avm/res/healthcare-apis/workspace/main.bicep index 4dbee8a189..475e814f77 100644 --- a/avm/res/healthcare-apis/workspace/main.bicep +++ b/avm/res/healthcare-apis/workspace/main.bicep @@ -81,7 +81,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/healthcare-apis/workspace/main.json b/avm/res/healthcare-apis/workspace/main.json index e3d6da5a0d..12635c0fb4 100644 --- a/avm/res/healthcare-apis/workspace/main.json +++ b/avm/res/healthcare-apis/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16749021786732296216" + "templateHash": "17512289027426655191" }, "name": "Healthcare API Workspaces", "description": "This module deploys a Healthcare API Workspace.", @@ -208,7 +208,7 @@ "FHIR SMART User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4ba50f17-9666-485c-a643-ff00808643f0')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -383,7 +383,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "1079296911965087473" + "templateHash": "17664239331720913904" }, "name": "Healthcare API Workspace FHIR Services", "description": "This module deploys a Healthcare API Workspace FHIR Service.", @@ -866,7 +866,7 @@ "FHIR SMART User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4ba50f17-9666-485c-a643-ff00808643f0')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/healthcare-apis/workspace/version.json b/avm/res/healthcare-apis/workspace/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/healthcare-apis/workspace/version.json +++ b/avm/res/healthcare-apis/workspace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/hybrid-compute/machine/main.bicep b/avm/res/hybrid-compute/machine/main.bicep index 69f476dc28..c406155aea 100644 --- a/avm/res/hybrid-compute/machine/main.bicep +++ b/avm/res/hybrid-compute/machine/main.bicep @@ -91,7 +91,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/hybrid-compute/machine/main.json b/avm/res/hybrid-compute/machine/main.json index 6cae863dae..7237f53e51 100644 --- a/avm/res/hybrid-compute/machine/main.json +++ b/avm/res/hybrid-compute/machine/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "3008449694375664417" + "templateHash": "10951579314283362107" }, "name": "Hybrid Compute Machines", "description": "This module deploys an Arc Machine for use with Arc Resource Bridge for Azure Stack HCI or VMware. In these scenarios, this resource module will be used in combination with another resource module to create the require Virtual Machine Instance extension resource on this Arc Machine resource. This module should not be used for other Arc-enabled server scenarios, where the Arc Machine resource is created automatically by the onboarding process.", @@ -270,7 +270,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", "Arc machine Administrator Login": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1c0163c0-47e6-4577-8991-ea5c82e286e4')]", "Arc machine Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]", diff --git a/avm/res/hybrid-compute/machine/version.json b/avm/res/hybrid-compute/machine/version.json index 729ac87673..76049e1c4a 100644 --- a/avm/res/hybrid-compute/machine/version.json +++ b/avm/res/hybrid-compute/machine/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/insights/action-group/main.bicep b/avm/res/insights/action-group/main.bicep index 1477f8c79f..a2f6315672 100644 --- a/avm/res/insights/action-group/main.bicep +++ b/avm/res/insights/action-group/main.bicep @@ -57,7 +57,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/insights/action-group/main.json b/avm/res/insights/action-group/main.json index fb7b2e5eb3..8bb0a64ce7 100644 --- a/avm/res/insights/action-group/main.json +++ b/avm/res/insights/action-group/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "3843866725906851395" + "templateHash": "239683209341595817" }, "name": "Action Groups", "description": "This module deploys an Action Group.", @@ -217,7 +217,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/insights/action-group/version.json b/avm/res/insights/action-group/version.json index c177b1bb58..3f863a2bec 100644 --- a/avm/res/insights/action-group/version.json +++ b/avm/res/insights/action-group/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/insights/activity-log-alert/main.bicep b/avm/res/insights/activity-log-alert/main.bicep index 1107cf6aed..6cfe1fee8f 100644 --- a/avm/res/insights/activity-log-alert/main.bicep +++ b/avm/res/insights/activity-log-alert/main.bicep @@ -45,7 +45,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/insights/activity-log-alert/main.json b/avm/res/insights/activity-log-alert/main.json index 725b8af89d..f878324c4e 100644 --- a/avm/res/insights/activity-log-alert/main.json +++ b/avm/res/insights/activity-log-alert/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "13226411448314617147" + "templateHash": "5015045274374664008" }, "name": "Activity Log Alerts", "description": "This module deploys an Activity Log Alert.", @@ -178,7 +178,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/insights/activity-log-alert/version.json b/avm/res/insights/activity-log-alert/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/insights/activity-log-alert/version.json +++ b/avm/res/insights/activity-log-alert/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/insights/data-collection-endpoint/main.bicep b/avm/res/insights/data-collection-endpoint/main.bicep index 6cb06c844f..f9a3eb3402 100644 --- a/avm/res/insights/data-collection-endpoint/main.bicep +++ b/avm/res/insights/data-collection-endpoint/main.bicep @@ -45,7 +45,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/insights/data-collection-endpoint/main.json b/avm/res/insights/data-collection-endpoint/main.json index 285f910d64..20f7d5a9b0 100644 --- a/avm/res/insights/data-collection-endpoint/main.json +++ b/avm/res/insights/data-collection-endpoint/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "6503238538933862648" + "templateHash": "3266035926509206513" }, "name": "Data Collection Endpoints", "description": "This module deploys a Data Collection Endpoint.", @@ -194,7 +194,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/insights/data-collection-endpoint/version.json b/avm/res/insights/data-collection-endpoint/version.json index c177b1bb58..3f863a2bec 100644 --- a/avm/res/insights/data-collection-endpoint/version.json +++ b/avm/res/insights/data-collection-endpoint/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/insights/metric-alert/main.bicep b/avm/res/insights/metric-alert/main.bicep index bd94d4d489..43027a01fb 100644 --- a/avm/res/insights/metric-alert/main.bicep +++ b/avm/res/insights/metric-alert/main.bicep @@ -87,7 +87,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/insights/metric-alert/main.json b/avm/res/insights/metric-alert/main.json index 71e0cbcc63..ed68723cfc 100644 --- a/avm/res/insights/metric-alert/main.json +++ b/avm/res/insights/metric-alert/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "5240734696906112690" + "templateHash": "4451159667263078094" }, "name": "Metric Alerts", "description": "This module deploys a Metric Alert.", @@ -309,7 +309,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/insights/metric-alert/version.json b/avm/res/insights/metric-alert/version.json index 9481fea58e..c177b1bb58 100644 --- a/avm/res/insights/metric-alert/version.json +++ b/avm/res/insights/metric-alert/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/insights/scheduled-query-rule/main.bicep b/avm/res/insights/scheduled-query-rule/main.bicep index de303d5e03..5d1e297435 100644 --- a/avm/res/insights/scheduled-query-rule/main.bicep +++ b/avm/res/insights/scheduled-query-rule/main.bicep @@ -80,7 +80,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/insights/scheduled-query-rule/main.json b/avm/res/insights/scheduled-query-rule/main.json index c79dd897c3..19364cabbc 100644 --- a/avm/res/insights/scheduled-query-rule/main.json +++ b/avm/res/insights/scheduled-query-rule/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "17672964122833676180" + "templateHash": "14700883695004277595" }, "name": "Scheduled Query Rules", "description": "This module deploys a Scheduled Query Rule.", @@ -255,7 +255,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/insights/scheduled-query-rule/version.json b/avm/res/insights/scheduled-query-rule/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/insights/scheduled-query-rule/version.json +++ b/avm/res/insights/scheduled-query-rule/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/insights/webtest/main.bicep b/avm/res/insights/webtest/main.bicep index 2bb6ca55cc..8f517215a2 100644 --- a/avm/res/insights/webtest/main.bicep +++ b/avm/res/insights/webtest/main.bicep @@ -88,7 +88,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/insights/webtest/main.json b/avm/res/insights/webtest/main.json index d957690c50..3cad10504a 100644 --- a/avm/res/insights/webtest/main.json +++ b/avm/res/insights/webtest/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.27.1.19265", - "templateHash": "7223657716552669834" + "version": "0.29.47.4906", + "templateHash": "5525260561297557547" }, "name": "Web Tests", "description": "This module deploys a Web Test.", @@ -277,7 +277,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/insights/webtest/version.json b/avm/res/insights/webtest/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/insights/webtest/version.json +++ b/avm/res/insights/webtest/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/key-vault/vault/key/main.bicep b/avm/res/key-vault/vault/key/main.bicep index 82eef70abd..45e00bf511 100644 --- a/avm/res/key-vault/vault/key/main.bicep +++ b/avm/res/key-vault/vault/key/main.bicep @@ -90,7 +90,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/key-vault/vault/key/main.json b/avm/res/key-vault/vault/key/main.json index 4292df4c7a..63c2159cb2 100644 --- a/avm/res/key-vault/vault/key/main.json +++ b/avm/res/key-vault/vault/key/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "10436564794447478489" + "templateHash": "14269695922191217406" }, "name": "Key Vault Keys", "description": "This module deploys a Key Vault Key.", @@ -216,7 +216,7 @@ "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/key-vault/vault/main.bicep b/avm/res/key-vault/vault/main.bicep index b804b867e7..12df3e45a3 100644 --- a/avm/res/key-vault/vault/main.bicep +++ b/avm/res/key-vault/vault/main.bicep @@ -129,7 +129,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/key-vault/vault/main.json b/avm/res/key-vault/vault/main.json index 15f25dc8bb..74ea3bdd03 100644 --- a/avm/res/key-vault/vault/main.json +++ b/avm/res/key-vault/vault/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "535497364453680203" + "templateHash": "8938543730613882040" }, "name": "Key Vaults", "description": "This module deploys a Key Vault.", @@ -1072,7 +1072,7 @@ "Key Vault Secrets User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -1494,7 +1494,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4990258423482296566" + "templateHash": "114626909766354577" }, "name": "Key Vault Secrets", "description": "This module deploys a Key Vault Secret.", @@ -1653,7 +1653,7 @@ "Key Vault Secrets User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -1792,7 +1792,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "10436564794447478489" + "templateHash": "14269695922191217406" }, "name": "Key Vault Keys", "description": "This module deploys a Key Vault Key.", @@ -2002,7 +2002,7 @@ "Key Vault Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '21090545-7ca7-4776-b22c-e363652d74d2')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/key-vault/vault/secret/main.bicep b/avm/res/key-vault/vault/secret/main.bicep index 2145488780..0371254fb4 100644 --- a/avm/res/key-vault/vault/secret/main.bicep +++ b/avm/res/key-vault/vault/secret/main.bicep @@ -55,7 +55,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/key-vault/vault/secret/main.json b/avm/res/key-vault/vault/secret/main.json index 8ae75bb1d3..b516966b2f 100644 --- a/avm/res/key-vault/vault/secret/main.json +++ b/avm/res/key-vault/vault/secret/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4990258423482296566" + "templateHash": "114626909766354577" }, "name": "Key Vault Secrets", "description": "This module deploys a Key Vault Secret.", @@ -165,7 +165,7 @@ "Key Vault Secrets User": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4633458b-17de-408a-b874-0445c86b69e6')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/key-vault/vault/version.json b/avm/res/key-vault/vault/version.json index 0f81d22abc..b8b30a0125 100644 --- a/avm/res/key-vault/vault/version.json +++ b/avm/res/key-vault/vault/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.8", + "version": "0.9", "pathFilters": [ "./main.json" ] diff --git a/avm/res/load-test-service/load-test/main.bicep b/avm/res/load-test-service/load-test/main.bicep index 3412a0df4c..d4a3aef67a 100644 --- a/avm/res/load-test-service/load-test/main.bicep +++ b/avm/res/load-test-service/load-test/main.bicep @@ -56,7 +56,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/load-test-service/load-test/main.json b/avm/res/load-test-service/load-test/main.json index 5f5d8ba0ab..0dcd38e20e 100644 --- a/avm/res/load-test-service/load-test/main.json +++ b/avm/res/load-test-service/load-test/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "10626999176786723279" + "templateHash": "3927348246772948246" }, "name": "Load Testing Service", "description": "This module deploys a Load test.", @@ -244,7 +244,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/load-test-service/load-test/version.json b/avm/res/load-test-service/load-test/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/load-test-service/load-test/version.json +++ b/avm/res/load-test-service/load-test/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/logic/workflow/main.bicep b/avm/res/logic/workflow/main.bicep index 4e95f611cd..c2e284fc99 100644 --- a/avm/res/logic/workflow/main.bicep +++ b/avm/res/logic/workflow/main.bicep @@ -106,7 +106,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/logic/workflow/main.json b/avm/res/logic/workflow/main.json index e434c0dc74..2e7d97ae2f 100644 --- a/avm/res/logic/workflow/main.json +++ b/avm/res/logic/workflow/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "1894000925881202684" + "templateHash": "7782212274562315083" }, "name": "Logic Apps (Workflows)", "description": "This module deploys a Logic App (Workflow).", @@ -450,7 +450,7 @@ "Logic App Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '515c2055-d9d4-4321-b1b9-bd0c9a0f79fe')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/logic/workflow/version.json b/avm/res/logic/workflow/version.json index c177b1bb58..3f863a2bec 100644 --- a/avm/res/logic/workflow/version.json +++ b/avm/res/logic/workflow/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/machine-learning-services/workspace/main.bicep b/avm/res/machine-learning-services/workspace/main.bicep index 6ff05fed5f..beadb6aa63 100644 --- a/avm/res/machine-learning-services/workspace/main.bicep +++ b/avm/res/machine-learning-services/workspace/main.bicep @@ -167,7 +167,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/machine-learning-services/workspace/main.json b/avm/res/machine-learning-services/workspace/main.json index 883c30b6d7..c90b0b9598 100644 --- a/avm/res/machine-learning-services/workspace/main.json +++ b/avm/res/machine-learning-services/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "3070187757867486541" + "templateHash": "6210663004291725942" }, "name": "Machine Learning Services Workspaces", "description": "This module deploys a Machine Learning Services Workspace.", @@ -1770,7 +1770,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/machine-learning-services/workspace/version.json b/avm/res/machine-learning-services/workspace/version.json index a89e5c9d3c..9a9a06e897 100644 --- a/avm/res/machine-learning-services/workspace/version.json +++ b/avm/res/machine-learning-services/workspace/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.7", + "version": "0.8", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/maintenance/maintenance-configuration/main.bicep b/avm/res/maintenance/maintenance-configuration/main.bicep index da1590a6bc..4eaa6fad29 100644 --- a/avm/res/maintenance/maintenance-configuration/main.bicep +++ b/avm/res/maintenance/maintenance-configuration/main.bicep @@ -63,7 +63,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/maintenance/maintenance-configuration/main.json b/avm/res/maintenance/maintenance-configuration/main.json index 651c20c00f..1dd639fd81 100644 --- a/avm/res/maintenance/maintenance-configuration/main.json +++ b/avm/res/maintenance/maintenance-configuration/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "9188458421094591722" + "templateHash": "14945501514189789066" }, "name": "Maintenance Configurations", "description": "This module deploys a Maintenance Configuration.", @@ -220,7 +220,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "Scheduled Patching Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'cd08ab90-6b14-449c-ad9a-8f8e549482c6')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } diff --git a/avm/res/maintenance/maintenance-configuration/version.json b/avm/res/maintenance/maintenance-configuration/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/maintenance/maintenance-configuration/version.json +++ b/avm/res/maintenance/maintenance-configuration/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/managed-identity/user-assigned-identity/main.bicep b/avm/res/managed-identity/user-assigned-identity/main.bicep index 1a5c7bbccf..0b73261730 100644 --- a/avm/res/managed-identity/user-assigned-identity/main.bicep +++ b/avm/res/managed-identity/user-assigned-identity/main.bicep @@ -35,7 +35,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/managed-identity/user-assigned-identity/main.json b/avm/res/managed-identity/user-assigned-identity/main.json index 1a1e10832f..739d726b2f 100644 --- a/avm/res/managed-identity/user-assigned-identity/main.json +++ b/avm/res/managed-identity/user-assigned-identity/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16128849797682252336" + "templateHash": "7772402327207512602" }, "name": "User Assigned Identities", "description": "This module deploys a User Assigned Identity.", @@ -209,7 +209,7 @@ "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/managed-identity/user-assigned-identity/version.json b/avm/res/managed-identity/user-assigned-identity/version.json index c177b1bb58..3f863a2bec 100644 --- a/avm/res/managed-identity/user-assigned-identity/version.json +++ b/avm/res/managed-identity/user-assigned-identity/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/net-app/net-app-account/capacity-pool/main.bicep b/avm/res/net-app/net-app-account/capacity-pool/main.bicep index 60125cc62f..3fafb3a6cd 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/main.bicep +++ b/avm/res/net-app/net-app-account/capacity-pool/main.bicep @@ -56,7 +56,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/net-app/net-app-account/capacity-pool/main.json b/avm/res/net-app/net-app-account/capacity-pool/main.json index b8ed42b918..34b2885f71 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/main.json +++ b/avm/res/net-app/net-app-account/capacity-pool/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "2991493712029822030" + "templateHash": "8361786183534097212" }, "name": "Azure NetApp Files Capacity Pools", "description": "This module deploys an Azure NetApp Files Capacity Pool.", @@ -195,7 +195,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -441,7 +441,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "6870432140728658293" + "templateHash": "18098024822158530053" }, "name": "Azure NetApp Files Capacity Pool Volumes", "description": "This module deploys an Azure NetApp Files Capacity Pool Volume.", @@ -907,7 +907,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/net-app/net-app-account/capacity-pool/volume/main.bicep b/avm/res/net-app/net-app-account/capacity-pool/volume/main.bicep index b9b26d20bf..8143e6e448 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/volume/main.bicep +++ b/avm/res/net-app/net-app-account/capacity-pool/volume/main.bicep @@ -189,7 +189,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/net-app/net-app-account/capacity-pool/volume/main.json b/avm/res/net-app/net-app-account/capacity-pool/volume/main.json index 968ee1993c..0e32725463 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/volume/main.json +++ b/avm/res/net-app/net-app-account/capacity-pool/volume/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "6870432140728658293" + "templateHash": "18098024822158530053" }, "name": "Azure NetApp Files Capacity Pool Volumes", "description": "This module deploys an Azure NetApp Files Capacity Pool Volume.", @@ -472,7 +472,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/net-app/net-app-account/main.bicep b/avm/res/net-app/net-app-account/main.bicep index 4f00ec4c93..d4f0a33392 100644 --- a/avm/res/net-app/net-app-account/main.bicep +++ b/avm/res/net-app/net-app-account/main.bicep @@ -104,7 +104,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/net-app/net-app-account/main.json b/avm/res/net-app/net-app-account/main.json index 73c191fcbd..afe617cbcf 100644 --- a/avm/res/net-app/net-app-account/main.json +++ b/avm/res/net-app/net-app-account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4309703310956110255" + "templateHash": "5247980225087680814" }, "name": "Azure NetApp Files", "description": "This module deploys an Azure NetApp File.", @@ -341,7 +341,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -504,7 +504,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "2991493712029822030" + "templateHash": "8361786183534097212" }, "name": "Azure NetApp Files Capacity Pools", "description": "This module deploys an Azure NetApp Files Capacity Pool.", @@ -693,7 +693,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -939,7 +939,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "6870432140728658293" + "templateHash": "18098024822158530053" }, "name": "Azure NetApp Files Capacity Pool Volumes", "description": "This module deploys an Azure NetApp Files Capacity Pool Volume.", @@ -1405,7 +1405,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/net-app/net-app-account/version.json b/avm/res/net-app/net-app-account/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/net-app/net-app-account/version.json +++ b/avm/res/net-app/net-app-account/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] From 66a71a40e0ac47efb1fe89d363412911ffcc886a Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Mon, 9 Sep 2024 00:26:34 +0200 Subject: [PATCH 04/57] fix: Kusto Cluster - Fixed identity output (#3220) ## Description The identity property does not always exist which is why the deployment fails for e.g. the defaults test. I assume the primary reason to add it was to return the System-Assigned-Identity information which has its own separate output and the identity ouptut can hence be removed. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.kusto.cluster](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.kusto.cluster.yml/badge.svg?branch=users%2Falsehr%2FkustoMsiFix&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.kusto.cluster.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [x] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --- avm/res/kusto/cluster/README.md | 1 - avm/res/kusto/cluster/main.bicep | 3 --- avm/res/kusto/cluster/main.json | 9 +-------- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/avm/res/kusto/cluster/README.md b/avm/res/kusto/cluster/README.md index dc7611acf7..8d94f1f46f 100644 --- a/avm/res/kusto/cluster/README.md +++ b/avm/res/kusto/cluster/README.md @@ -1699,7 +1699,6 @@ The resource ID of the subnet to which to deploy the Kusto Cluster. | Output | Type | Description | | :-- | :-- | :-- | -| `identity` | object | The identity of the cluster. | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the kusto cluster. | | `privateEndpoints` | array | The private endpoints of the kusto cluster. | diff --git a/avm/res/kusto/cluster/main.bicep b/avm/res/kusto/cluster/main.bicep index b7693cf104..e1372dca57 100644 --- a/avm/res/kusto/cluster/main.bicep +++ b/avm/res/kusto/cluster/main.bicep @@ -375,9 +375,6 @@ output name string = kustoCluster.name @description('The location the resource was deployed into.') output location string = kustoCluster.location -@description('The identity of the cluster.') -output identity object = kustoCluster.identity - @description('The private endpoints of the kusto cluster.') output privateEndpoints array = [ for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { diff --git a/avm/res/kusto/cluster/main.json b/avm/res/kusto/cluster/main.json index 99f4dd208c..2c06ed215d 100644 --- a/avm/res/kusto/cluster/main.json +++ b/avm/res/kusto/cluster/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "9987903523872780142" + "templateHash": "17272299626990757362" }, "name": "Kusto Cluster", "description": "This module deploys a Kusto Cluster.", @@ -1977,13 +1977,6 @@ }, "value": "[reference('kustoCluster', '2023-08-15', 'full').location]" }, - "identity": { - "type": "object", - "metadata": { - "description": "The identity of the cluster." - }, - "value": "[reference('kustoCluster', '2023-08-15', 'full').identity]" - }, "privateEndpoints": { "type": "array", "metadata": { From ff08d9847602d8f704018913ef5274ec5fa9cd65 Mon Sep 17 00:00:00 2001 From: Kris Baranek <20225789+krbar@users.noreply.github.com> Date: Mon, 9 Sep 2024 09:01:37 +0200 Subject: [PATCH 05/57] feat: Add identity support for `avm/res/insigths/data-collection-rule` module (#3230) ## Description - add identity support for the `avm/res/insigths/data-collection-rule` module - update api version in test cases ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.insights.data-collection-rule](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.insights.data-collection-rule.yml/badge.svg?branch=user%2Fkrbar%2FdcrIdentity)](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.insights.data-collection-rule.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [x] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../insights/data-collection-rule/README.md | 52 +++++++++++++++++++ .../insights/data-collection-rule/main.bicep | 32 ++++++++++++ .../insights/data-collection-rule/main.json | 42 ++++++++++++++- .../tests/e2e/agent-settings/main.test.bicep | 2 +- .../tests/e2e/customadv/dependencies.bicep | 4 +- .../tests/e2e/customadv/main.test.bicep | 5 +- .../tests/e2e/custombasic/dependencies.bicep | 4 +- .../tests/e2e/custombasic/main.test.bicep | 2 +- .../tests/e2e/customiis/dependencies.bicep | 4 +- .../tests/e2e/customiis/main.test.bicep | 2 +- .../tests/e2e/defaults/main.test.bicep | 2 +- .../tests/e2e/linux/dependencies.bicep | 2 +- .../tests/e2e/linux/main.test.bicep | 2 +- .../tests/e2e/max/dependencies.bicep | 9 ++-- .../tests/e2e/max/main.test.bicep | 8 ++- .../tests/e2e/waf-aligned/dependencies.bicep | 2 +- .../tests/e2e/waf-aligned/main.test.bicep | 2 +- .../tests/e2e/windows/dependencies.bicep | 2 +- .../tests/e2e/windows/main.test.bicep | 2 +- .../data-collection-rule/version.json | 2 +- 20 files changed, 159 insertions(+), 23 deletions(-) diff --git a/avm/res/insights/data-collection-rule/README.md b/avm/res/insights/data-collection-rule/README.md index 07e3b9bc40..6134656d6d 100644 --- a/avm/res/insights/data-collection-rule/README.md +++ b/avm/res/insights/data-collection-rule/README.md @@ -202,6 +202,9 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' + managedIdentities: { + systemAssigned: true + } tags: { 'hidden-title': 'This is visible in the resource name' kind: 'Windows' @@ -308,6 +311,11 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" }, + "managedIdentities": { + "value": { + "systemAssigned": true + } + }, "tags": { "value": { "hidden-title": "This is visible in the resource name", @@ -1216,6 +1224,12 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:' + ] + } roleAssignments: [ { name: '89a4d6fa-defb-4099-9196-173d94b91d67' @@ -1331,6 +1345,14 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule:" + ] + } + }, "roleAssignments": { "value": [ { @@ -1926,6 +1948,7 @@ module dataCollectionRule 'br/public:avm/res/insights/data-collection-rule: { '${id}': {} }), + {}, + (cur, next) => union(cur, next) +) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} } +var identity = !empty(managedIdentities) + ? { + type: (managedIdentities.?systemAssigned ?? false) + ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') + : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : 'None') + userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null + } + : null + var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') @@ -89,6 +106,7 @@ resource dataCollectionRule 'Microsoft.Insights/dataCollectionRules@2023-03-11' location: location name: name tags: tags + identity: identity properties: dataCollectionRulePropertiesUnion } @@ -97,6 +115,7 @@ resource dataCollectionRuleAll 'Microsoft.Insights/dataCollectionRules@2023-03-1 location: location name: name tags: tags + identity: identity properties: dataCollectionRulePropertiesUnion } @@ -131,12 +150,25 @@ output location string = dataCollectionRuleProperties.kind == 'All' ? dataCollectionRuleAll.location : dataCollectionRule.location +@description('The principal ID of the system assigned identity.') +output systemAssignedMIPrincipalId string = dataCollectionRuleProperties.kind == 'All' + ? dataCollectionRuleAll.?identity.?principalId ?? '' + : dataCollectionRule.?identity.?principalId ?? '' + // =============== // // Definitions // // =============== // import { roleAssignmentType, lockType } from 'modules/nested_conditionalScope.bicep' +type managedIdentitiesType = { + @description('Optional. Enables system assigned managed identity on the resource.') + systemAssigned: bool? + + @description('Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption.') + userAssignedResourceIds: string[]? +}? + @discriminator('kind') type dataCollectionRulePropertiesType = | linuxDcrPropertiesType diff --git a/avm/res/insights/data-collection-rule/main.json b/avm/res/insights/data-collection-rule/main.json index ffb2fd24de..c9b904f82f 100644 --- a/avm/res/insights/data-collection-rule/main.json +++ b/avm/res/insights/data-collection-rule/main.json @@ -6,13 +6,36 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "9024628264172390673" + "templateHash": "6159067500010827927" }, "name": "Data Collection Rules", "description": "This module deploys a Data Collection Rule.", "owner": "Azure/module-maintainers" }, "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + }, + "nullable": true + }, "dataCollectionRulePropertiesType": { "type": "object", "discriminator": { @@ -396,6 +419,12 @@ "description": "Optional. The lock settings of the service." } }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource. Only one type of, and up to one managed identity is supported." + } + }, "roleAssignments": { "$ref": "#/definitions/roleAssignmentType", "metadata": { @@ -411,6 +440,8 @@ } }, "variables": { + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", @@ -449,6 +480,7 @@ "kind": "[parameters('dataCollectionRuleProperties').kind]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", "properties": "[variables('dataCollectionRulePropertiesUnion')]" }, "dataCollectionRuleAll": { @@ -458,6 +490,7 @@ "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", "properties": "[variables('dataCollectionRulePropertiesUnion')]" }, "dataCollectionRule_conditionalScopeResources": { @@ -715,6 +748,13 @@ "description": "The location the resource was deployed into." }, "value": "[if(equals(parameters('dataCollectionRuleProperties').kind, 'All'), reference('dataCollectionRuleAll', '2023-03-11', 'full').location, reference('dataCollectionRule', '2023-03-11', 'full').location)]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[if(equals(parameters('dataCollectionRuleProperties').kind, 'All'), coalesce(tryGet(tryGet(reference('dataCollectionRuleAll', '2023-03-11', 'full'), 'identity'), 'principalId'), ''), coalesce(tryGet(tryGet(reference('dataCollectionRule', '2023-03-11', 'full'), 'identity'), 'principalId'), ''))]" } } } \ No newline at end of file diff --git a/avm/res/insights/data-collection-rule/tests/e2e/agent-settings/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/agent-settings/main.test.bicep index b618848eb8..28364a28f0 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/agent-settings/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/agent-settings/main.test.bicep @@ -25,7 +25,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { name: resourceGroupName location: resourceLocation } diff --git a/avm/res/insights/data-collection-rule/tests/e2e/customadv/dependencies.bicep b/avm/res/insights/data-collection-rule/tests/e2e/customadv/dependencies.bicep index 35ea4ba120..83a92b025f 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/customadv/dependencies.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/customadv/dependencies.bicep @@ -7,7 +7,7 @@ param dataCollectionEndpointName string @description('Required. The name of the log analytics workspace to create.') param logAnalyticsWorkspaceName string -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { name: logAnalyticsWorkspaceName location: location @@ -47,7 +47,7 @@ resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10 } } -resource dataCollectionEndpoint 'Microsoft.Insights/dataCollectionEndpoints@2021-04-01' = { +resource dataCollectionEndpoint 'Microsoft.Insights/dataCollectionEndpoints@2023-03-11' = { kind: 'Windows' location: location name: dataCollectionEndpointName diff --git a/avm/res/insights/data-collection-rule/tests/e2e/customadv/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/customadv/main.test.bicep index 97004509b5..23b27402bf 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/customadv/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/customadv/main.test.bicep @@ -25,7 +25,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { name: resourceGroupName location: resourceLocation } @@ -127,6 +127,9 @@ module testDeployment '../../../main.bicep' = [ } } } + managedIdentities: { + systemAssigned: true + } tags: { 'hidden-title': 'This is visible in the resource name' resourceType: 'Data Collection Rules' diff --git a/avm/res/insights/data-collection-rule/tests/e2e/custombasic/dependencies.bicep b/avm/res/insights/data-collection-rule/tests/e2e/custombasic/dependencies.bicep index 0412e61164..b474947ee3 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/custombasic/dependencies.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/custombasic/dependencies.bicep @@ -7,7 +7,7 @@ param dataCollectionEndpointName string @description('Required. The name of the log analytics workspace to create.') param logAnalyticsWorkspaceName string -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { name: logAnalyticsWorkspaceName location: location @@ -31,7 +31,7 @@ resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10 } } -resource dataCollectionEndpoint 'Microsoft.Insights/dataCollectionEndpoints@2021-04-01' = { +resource dataCollectionEndpoint 'Microsoft.Insights/dataCollectionEndpoints@2023-03-11' = { kind: 'Windows' location: location name: dataCollectionEndpointName diff --git a/avm/res/insights/data-collection-rule/tests/e2e/custombasic/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/custombasic/main.test.bicep index cdd52f593e..7c660c4be1 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/custombasic/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/custombasic/main.test.bicep @@ -25,7 +25,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { name: resourceGroupName location: resourceLocation } diff --git a/avm/res/insights/data-collection-rule/tests/e2e/customiis/dependencies.bicep b/avm/res/insights/data-collection-rule/tests/e2e/customiis/dependencies.bicep index b12b662cc7..a4a121e567 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/customiis/dependencies.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/customiis/dependencies.bicep @@ -7,12 +7,12 @@ param dataCollectionEndpointName string @description('Required. The name of the log analytics workspace to create.') param logAnalyticsWorkspaceName string -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { name: logAnalyticsWorkspaceName location: location } -resource dataCollectionEndpoint 'Microsoft.Insights/dataCollectionEndpoints@2021-04-01' = { +resource dataCollectionEndpoint 'Microsoft.Insights/dataCollectionEndpoints@2023-03-11' = { kind: 'Windows' location: location name: dataCollectionEndpointName diff --git a/avm/res/insights/data-collection-rule/tests/e2e/customiis/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/customiis/main.test.bicep index 017061171a..21221f6e68 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/customiis/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/customiis/main.test.bicep @@ -25,7 +25,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { name: resourceGroupName location: resourceLocation } diff --git a/avm/res/insights/data-collection-rule/tests/e2e/defaults/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/defaults/main.test.bicep index 6c6fd466da..50102a6927 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/defaults/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/defaults/main.test.bicep @@ -25,7 +25,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { name: resourceGroupName location: resourceLocation } diff --git a/avm/res/insights/data-collection-rule/tests/e2e/linux/dependencies.bicep b/avm/res/insights/data-collection-rule/tests/e2e/linux/dependencies.bicep index c20b584c10..62da14da45 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/linux/dependencies.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/linux/dependencies.bicep @@ -4,7 +4,7 @@ param location string = resourceGroup().location @description('Required. The name of the log analytics workspace to create.') param logAnalyticsWorkspaceName string -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { name: logAnalyticsWorkspaceName location: location } diff --git a/avm/res/insights/data-collection-rule/tests/e2e/linux/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/linux/main.test.bicep index 0371f75541..4a7759ac31 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/linux/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/linux/main.test.bicep @@ -25,7 +25,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { name: resourceGroupName location: resourceLocation } diff --git a/avm/res/insights/data-collection-rule/tests/e2e/max/dependencies.bicep b/avm/res/insights/data-collection-rule/tests/e2e/max/dependencies.bicep index e7a3cd006b..e2e3291fdd 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/max/dependencies.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/max/dependencies.bicep @@ -10,7 +10,7 @@ param logAnalyticsWorkspaceName string @description('Required. The name of the managed identity to create.') param managedIdentityName string -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { name: logAnalyticsWorkspaceName location: location @@ -34,12 +34,12 @@ resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10 } } -resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { name: managedIdentityName location: location } -resource dataCollectionEndpoint 'Microsoft.Insights/dataCollectionEndpoints@2021-04-01' = { +resource dataCollectionEndpoint 'Microsoft.Insights/dataCollectionEndpoints@2023-03-11' = { kind: 'Windows' location: location name: dataCollectionEndpointName @@ -59,5 +59,8 @@ 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 Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + @description('The resource ID of the created Data Collection Endpoint.') output dataCollectionEndpointResourceId string = dataCollectionEndpoint.id diff --git a/avm/res/insights/data-collection-rule/tests/e2e/max/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/max/main.test.bicep index 27ac14b5c4..41c799217a 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/max/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/max/main.test.bicep @@ -25,7 +25,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { name: resourceGroupName location: resourceLocation } @@ -116,6 +116,12 @@ module testDeployment '../../../main.bicep' = [ kind: 'CanNotDelete' name: 'myCustomLockName' } + managedIdentities: { + systemAssigned: false + userAssignedResourceIds: [ + nestedDependencies.outputs.managedIdentityResourceId + ] + } roleAssignments: [ { name: '89a4d6fa-defb-4099-9196-173d94b91d67' diff --git a/avm/res/insights/data-collection-rule/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/insights/data-collection-rule/tests/e2e/waf-aligned/dependencies.bicep index c20b584c10..62da14da45 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/waf-aligned/dependencies.bicep @@ -4,7 +4,7 @@ param location string = resourceGroup().location @description('Required. The name of the log analytics workspace to create.') param logAnalyticsWorkspaceName string -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { name: logAnalyticsWorkspaceName location: location } diff --git a/avm/res/insights/data-collection-rule/tests/e2e/waf-aligned/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/waf-aligned/main.test.bicep index 797344b7e1..d4cec148c1 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/waf-aligned/main.test.bicep @@ -26,7 +26,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { name: resourceGroupName location: resourceLocation } diff --git a/avm/res/insights/data-collection-rule/tests/e2e/windows/dependencies.bicep b/avm/res/insights/data-collection-rule/tests/e2e/windows/dependencies.bicep index c20b584c10..62da14da45 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/windows/dependencies.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/windows/dependencies.bicep @@ -4,7 +4,7 @@ param location string = resourceGroup().location @description('Required. The name of the log analytics workspace to create.') param logAnalyticsWorkspaceName string -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { name: logAnalyticsWorkspaceName location: location } diff --git a/avm/res/insights/data-collection-rule/tests/e2e/windows/main.test.bicep b/avm/res/insights/data-collection-rule/tests/e2e/windows/main.test.bicep index 7c58e5ede8..0655a2c2fd 100644 --- a/avm/res/insights/data-collection-rule/tests/e2e/windows/main.test.bicep +++ b/avm/res/insights/data-collection-rule/tests/e2e/windows/main.test.bicep @@ -25,7 +25,7 @@ param namePrefix string = '#_namePrefix_#' // General resources // ================= -resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { name: resourceGroupName location: resourceLocation } diff --git a/avm/res/insights/data-collection-rule/version.json b/avm/res/insights/data-collection-rule/version.json index c177b1bb58..3f863a2bec 100644 --- a/avm/res/insights/data-collection-rule/version.json +++ b/avm/res/insights/data-collection-rule/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] From 7990280450653fa60278cb6e9a130c8053d199fa Mon Sep 17 00:00:00 2001 From: Ahmad Abdalla <28486158+ahmadabdalla@users.noreply.github.com> Date: Mon, 9 Sep 2024 17:04:13 +1000 Subject: [PATCH 06/57] fix: Update `avm/res/dev-test-lab/lab` module virtual network parameters and increased test coverage. (#3180) ## Description Closes #3126 - Update optional parameters for the virtual network child module. - Increase test coverage for the max test cases to cater for optional scenarios. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.dev-test-lab.lab](https://github.com/ahmadabdalla/bicep-registry-modules/actions/workflows/avm.res.dev-test-lab.lab.yml/badge.svg?branch=users%2Fahmad%2F3126_DTL)](https://github.com/ahmadabdalla/bicep-registry-modules/actions/workflows/avm.res.dev-test-lab.lab.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [x] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/dev-test-lab/lab/README.md | 30 +- avm/res/dev-test-lab/lab/main.bicep | 4 +- avm/res/dev-test-lab/lab/main.json | 426 +++++++++--------- .../lab/tests/e2e/max/dependencies.bicep | 24 +- .../lab/tests/e2e/max/main.test.bicep | 25 +- .../dev-test-lab/lab/virtualnetwork/README.md | 160 ++++++- .../lab/virtualnetwork/main.bicep | 12 +- .../dev-test-lab/lab/virtualnetwork/main.json | 213 ++++----- 8 files changed, 558 insertions(+), 336 deletions(-) diff --git a/avm/res/dev-test-lab/lab/README.md b/avm/res/dev-test-lab/lab/README.md index da4135a4ff..22a277da2a 100644 --- a/avm/res/dev-test-lab/lab/README.md +++ b/avm/res/dev-test-lab/lab/README.md @@ -149,6 +149,8 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { target: 450 thresholdValue100DisplayOnChart: 'Enabled' thresholdValue100SendNotificationWhenExceeded: 'Enabled' + thresholdValue125DisplayOnChart: 'Disabled' + thresholdValue75DisplayOnChart: 'Enabled' } disableAutoUpgradeCseMinorVersion: true encryptionDiskEncryptionSetId: '' @@ -320,6 +322,11 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { labSubnetName: '' resourceId: '' } + { + allowPublicIp: 'Deny' + labSubnetName: '' + resourceId: '' + } ] description: 'lab virtual network description' externalProviderResourceId: '' @@ -343,6 +350,12 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { useInVmCreationPermission: 'Allow' usePublicIpAddressPermission: 'Allow' } + { + labSubnetName: '' + resourceId: '' + useInVmCreationPermission: 'Deny' + usePublicIpAddressPermission: 'Deny' + } ] } ] @@ -425,7 +438,9 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { "status": "Enabled", "target": 450, "thresholdValue100DisplayOnChart": "Enabled", - "thresholdValue100SendNotificationWhenExceeded": "Enabled" + "thresholdValue100SendNotificationWhenExceeded": "Enabled", + "thresholdValue125DisplayOnChart": "Disabled", + "thresholdValue75DisplayOnChart": "Enabled" } }, "disableAutoUpgradeCseMinorVersion": { @@ -634,6 +649,11 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { "allowPublicIp": "Allow", "labSubnetName": "", "resourceId": "" + }, + { + "allowPublicIp": "Deny", + "labSubnetName": "", + "resourceId": "" } ], "description": "lab virtual network description", @@ -657,6 +677,12 @@ module lab 'br/public:avm/res/dev-test-lab/lab:' = { }, "useInVmCreationPermission": "Allow", "usePublicIpAddressPermission": "Allow" + }, + { + "labSubnetName": "", + "resourceId": "", + "useInVmCreationPermission": "Deny", + "usePublicIpAddressPermission": "Deny" } ] } @@ -2026,7 +2052,7 @@ The resource ID of the subnet. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)). -- Required: Yes +- Required: No - Type: object **Required parameters** diff --git a/avm/res/dev-test-lab/lab/main.bicep b/avm/res/dev-test-lab/lab/main.bicep index 3d0fe3a7ce..186bb5773c 100644 --- a/avm/res/dev-test-lab/lab/main.bicep +++ b/avm/res/dev-test-lab/lab/main.bicep @@ -461,10 +461,10 @@ type virtualNetworkType = { description: string? @description('Optional. The allowed subnets of the virtual network.') - allowedSubnets: allowedSubnetType[]? + allowedSubnets: allowedSubnetType? @description('Optional. The subnet overrides of the virtual network.') - subnetOverrides: subnetOverrideType[]? + subnetOverrides: subnetOverrideType? }[]? type costsType = { diff --git a/avm/res/dev-test-lab/lab/main.json b/avm/res/dev-test-lab/lab/main.json index 711f287f08..f1245a9d6e 100644 --- a/avm/res/dev-test-lab/lab/main.json +++ b/avm/res/dev-test-lab/lab/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "9164793528274272981" + "templateHash": "15435393299266858028" }, "name": "DevTest Labs", "description": "This module deploys a DevTest Lab.", @@ -244,20 +244,14 @@ } }, "allowedSubnets": { - "type": "array", - "items": { - "$ref": "#/definitions/allowedSubnetType" - }, + "$ref": "#/definitions/allowedSubnetType", "nullable": true, "metadata": { "description": "Optional. The allowed subnets of the virtual network." } }, "subnetOverrides": { - "type": "array", - "items": { - "$ref": "#/definitions/subnetOverrideType" - }, + "$ref": "#/definitions/subnetOverrideType", "nullable": true, "metadata": { "description": "Optional. The subnet overrides of the virtual network." @@ -666,33 +660,37 @@ "nullable": true }, "allowedSubnetType": { - "type": "object", - "properties": { - "allowPublicIp": { - "type": "string", - "allowedValues": [ - "Allow", - "Default", - "Deny" - ], - "nullable": true, - "metadata": { - "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the allowed subnet." - } - }, - "labSubnetName": { - "type": "string", - "metadata": { - "description": "Required. The name of the subnet as seen in the lab." + "type": "array", + "items": { + "type": "object", + "properties": { + "allowPublicIp": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the allowed subnet." + } + }, + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name of the subnet as seen in the lab." + } } } }, + "nullable": true, "metadata": { "__bicep_imported_from!": { "sourceTemplate": "virtualnetwork/main.bicep" @@ -784,87 +782,92 @@ } }, "subnetOverrideType": { - "type": "object", - "properties": { - "labSubnetName": { - "type": "string", - "metadata": { - "description": "Required. The name given to the subnet within the lab." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the subnet." - } - }, - "sharedPublicIpAddressConfiguration": { - "type": "object", - "properties": { - "allowedPorts": { - "type": "array", - "items": { - "type": "object", - "properties": { - "backendPort": { - "type": "int", - "metadata": { - "description": "Required. Backend port of the target virtual machine." - } - }, - "transportProtocol": { - "type": "string", - "allowedValues": [ - "Tcp", - "Udp" - ], - "metadata": { - "description": "Required. Protocol type of the port." + "type": "array", + "items": { + "type": "object", + "properties": { + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name given to the subnet within the lab." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the subnet." + } + }, + "sharedPublicIpAddressConfiguration": { + "type": "object", + "properties": { + "allowedPorts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "backendPort": { + "type": "int", + "metadata": { + "description": "Required. Backend port of the target virtual machine." + } + }, + "transportProtocol": { + "type": "string", + "allowedValues": [ + "Tcp", + "Udp" + ], + "metadata": { + "description": "Required. Protocol type of the port." + } } } + }, + "metadata": { + "description": "Required. Backend ports that virtual machines on this subnet are allowed to expose." } - }, - "metadata": { - "description": "Required. Backend ports that virtual machines on this subnet are allowed to expose." } + }, + "nullable": true, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." } }, - "metadata": { - "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." - } - }, - "useInVmCreationPermission": { - "type": "string", - "allowedValues": [ - "Allow", - "Default", - "Deny" - ], - "nullable": true, - "metadata": { - "description": "Optional. Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny)." - } - }, - "usePublicIpAddressPermission": { - "type": "string", - "allowedValues": [ - "Allow", - "Default", - "Deny" - ], - "nullable": true, - "metadata": { - "description": "Optional. Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny)." - } - }, - "virtualNetworkPoolName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The virtual network pool associated with this subnet." + "useInVmCreationPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny)." + } + }, + "usePublicIpAddressPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny)." + } + }, + "virtualNetworkPoolName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The virtual network pool associated with this subnet." + } } } }, + "nullable": true, "metadata": { "__bicep_imported_from!": { "sourceTemplate": "virtualnetwork/main.bicep" @@ -1272,127 +1275,136 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "18309816581107210302" + "templateHash": "12122718661184299591" }, "name": "DevTest Lab Virtual Networks", - "description": "This module deploys a DevTest Lab Virtual Network.\r\n\r\nLab virtual machines must be deployed into a virtual network. This resource type allows configuring the virtual network and subnet settings used for the lab virtual machines.", + "description": "This module deploys a DevTest Lab Virtual Network.\n\nLab virtual machines must be deployed into a virtual network. This resource type allows configuring the virtual network and subnet settings used for the lab virtual machines.", "owner": "Azure/module-maintainers" }, "definitions": { "allowedSubnetType": { - "type": "object", - "properties": { - "allowPublicIp": { - "type": "string", - "allowedValues": [ - "Allow", - "Default", - "Deny" - ], - "nullable": true, - "metadata": { - "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the allowed subnet." - } - }, - "labSubnetName": { - "type": "string", - "metadata": { - "description": "Required. The name of the subnet as seen in the lab." + "type": "array", + "items": { + "type": "object", + "properties": { + "allowPublicIp": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the allowed subnet." + } + }, + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name of the subnet as seen in the lab." + } } } }, + "nullable": true, "metadata": { "__bicep_export!": true } }, "subnetOverrideType": { - "type": "object", - "properties": { - "labSubnetName": { - "type": "string", - "metadata": { - "description": "Required. The name given to the subnet within the lab." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the subnet." - } - }, - "sharedPublicIpAddressConfiguration": { - "type": "object", - "properties": { - "allowedPorts": { - "type": "array", - "items": { - "type": "object", - "properties": { - "backendPort": { - "type": "int", - "metadata": { - "description": "Required. Backend port of the target virtual machine." - } - }, - "transportProtocol": { - "type": "string", - "allowedValues": [ - "Tcp", - "Udp" - ], - "metadata": { - "description": "Required. Protocol type of the port." + "type": "array", + "items": { + "type": "object", + "properties": { + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name given to the subnet within the lab." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the subnet." + } + }, + "sharedPublicIpAddressConfiguration": { + "type": "object", + "properties": { + "allowedPorts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "backendPort": { + "type": "int", + "metadata": { + "description": "Required. Backend port of the target virtual machine." + } + }, + "transportProtocol": { + "type": "string", + "allowedValues": [ + "Tcp", + "Udp" + ], + "metadata": { + "description": "Required. Protocol type of the port." + } } } + }, + "metadata": { + "description": "Required. Backend ports that virtual machines on this subnet are allowed to expose." } - }, - "metadata": { - "description": "Required. Backend ports that virtual machines on this subnet are allowed to expose." } + }, + "nullable": true, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." } }, - "metadata": { - "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." - } - }, - "useInVmCreationPermission": { - "type": "string", - "allowedValues": [ - "Allow", - "Default", - "Deny" - ], - "nullable": true, - "metadata": { - "description": "Optional. Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny)." - } - }, - "usePublicIpAddressPermission": { - "type": "string", - "allowedValues": [ - "Allow", - "Default", - "Deny" - ], - "nullable": true, - "metadata": { - "description": "Optional. Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny)." - } - }, - "virtualNetworkPoolName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The virtual network pool associated with this subnet." + "useInVmCreationPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny)." + } + }, + "usePublicIpAddressPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny)." + } + }, + "virtualNetworkPoolName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The virtual network pool associated with this subnet." + } } } }, + "nullable": true, "metadata": { "__bicep_export!": true } @@ -1426,21 +1438,19 @@ }, "description": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The description of the virtual network." } }, "allowedSubnets": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/allowedSubnetType", "metadata": { "description": "Optional. The allowed subnets of the virtual network." } }, "subnetOverrides": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/subnetOverrideType", "metadata": { "description": "Optional. The subnet overrides of the virtual network." } diff --git a/avm/res/dev-test-lab/lab/tests/e2e/max/dependencies.bicep b/avm/res/dev-test-lab/lab/tests/e2e/max/dependencies.bicep index 5e1c0b1d25..4c97fabff5 100644 --- a/avm/res/dev-test-lab/lab/tests/e2e/max/dependencies.bicep +++ b/avm/res/dev-test-lab/lab/tests/e2e/max/dependencies.bicep @@ -103,9 +103,15 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { } subnets: [ { - name: 'defaultSubnet' + name: 'subnet01' properties: { - addressPrefix: cidrSubnet(addressPrefix, 16, 0) + addressPrefix: cidrSubnet(addressPrefix, 24, 0) + } + } + { + name: 'subnet02' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 1) } } ] @@ -118,11 +124,17 @@ output virtualNetworkName string = virtualNetwork.name @description('The resource ID of the created Virtual Network.') output virtualNetworkResourceId string = virtualNetwork.id -@description('The name of the created Virtual Network Subnet.') -output subnetName string = virtualNetwork.properties.subnets[0].name +@description('The name of the created Virtual Network Subnet 1.') +output subnet1Name string = virtualNetwork.properties.subnets[0].name + +@description('The resource ID of the created Virtual Network Subnet 1.') +output subnet1ResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The name of the created Virtual Network Subnet 2.') +output subnet2Name string = virtualNetwork.properties.subnets[1].name -@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 Virtual Network Subnet 2.') +output subnet2ResourceId string = virtualNetwork.properties.subnets[1].id @description('The principal ID of the created Managed Identity.') output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/res/dev-test-lab/lab/tests/e2e/max/main.test.bicep b/avm/res/dev-test-lab/lab/tests/e2e/max/main.test.bicep index 65d06f3e05..fc069f9248 100644 --- a/avm/res/dev-test-lab/lab/tests/e2e/max/main.test.bicep +++ b/avm/res/dev-test-lab/lab/tests/e2e/max/main.test.bicep @@ -129,15 +129,20 @@ module testDeployment '../../../main.bicep' = [ description: 'lab virtual network description' allowedSubnets: [ { - labSubnetName: nestedDependencies.outputs.subnetName - resourceId: nestedDependencies.outputs.subnetResourceId + labSubnetName: nestedDependencies.outputs.subnet1Name + resourceId: nestedDependencies.outputs.subnet1ResourceId allowPublicIp: 'Allow' } + { + labSubnetName: nestedDependencies.outputs.subnet2Name + resourceId: nestedDependencies.outputs.subnet2ResourceId + allowPublicIp: 'Deny' + } ] subnetOverrides: [ { - labSubnetName: nestedDependencies.outputs.subnetName - resourceId: nestedDependencies.outputs.subnetResourceId + labSubnetName: nestedDependencies.outputs.subnet1Name + resourceId: nestedDependencies.outputs.subnet1ResourceId useInVmCreationPermission: 'Allow' usePublicIpAddressPermission: 'Allow' sharedPublicIpAddressConfiguration: { @@ -153,14 +158,20 @@ module testDeployment '../../../main.bicep' = [ ] } } + { + labSubnetName: nestedDependencies.outputs.subnet2Name + resourceId: nestedDependencies.outputs.subnet2ResourceId + useInVmCreationPermission: 'Deny' + usePublicIpAddressPermission: 'Deny' + } ] } ] policies: [ { - name: nestedDependencies.outputs.subnetName + name: nestedDependencies.outputs.subnet1Name evaluatorType: 'MaxValuePolicy' - factData: nestedDependencies.outputs.subnetResourceId + factData: nestedDependencies.outputs.subnet1ResourceId factName: 'UserOwnedLabVmCountInSubnet' threshold: '1' } @@ -308,6 +319,8 @@ module testDeployment '../../../main.bicep' = [ currencyCode: 'AUD' thresholdValue100DisplayOnChart: 'Enabled' thresholdValue100SendNotificationWhenExceeded: 'Enabled' + thresholdValue125DisplayOnChart: 'Disabled' + thresholdValue75DisplayOnChart: 'Enabled' } } } diff --git a/avm/res/dev-test-lab/lab/virtualnetwork/README.md b/avm/res/dev-test-lab/lab/virtualnetwork/README.md index 6fa8c79a9a..203f59111b 100644 --- a/avm/res/dev-test-lab/lab/virtualnetwork/README.md +++ b/avm/res/dev-test-lab/lab/virtualnetwork/README.md @@ -67,7 +67,48 @@ The allowed subnets of the virtual network. - Required: No - Type: array -- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`labSubnetName`](#parameter-allowedsubnetslabsubnetname) | string | The name of the subnet as seen in the lab. | +| [`resourceId`](#parameter-allowedsubnetsresourceid) | string | The resource ID of the allowed subnet. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowPublicIp`](#parameter-allowedsubnetsallowpublicip) | string | The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)). | + +### Parameter: `allowedSubnets.labSubnetName` + +The name of the subnet as seen in the lab. + +- Required: Yes +- Type: string + +### Parameter: `allowedSubnets.resourceId` + +The resource ID of the allowed subnet. + +- Required: Yes +- Type: string + +### Parameter: `allowedSubnets.allowPublicIp` + +The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)). + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Allow' + 'Default' + 'Deny' + ] + ``` ### Parameter: `description` @@ -75,7 +116,6 @@ The description of the virtual network. - Required: No - Type: string -- Default: `''` ### Parameter: `subnetOverrides` @@ -83,7 +123,121 @@ The subnet overrides of the virtual network. - Required: No - Type: array -- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`labSubnetName`](#parameter-subnetoverrideslabsubnetname) | string | The name given to the subnet within the lab. | +| [`resourceId`](#parameter-subnetoverridesresourceid) | string | The resource ID of the subnet. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`sharedPublicIpAddressConfiguration`](#parameter-subnetoverridessharedpublicipaddressconfiguration) | object | The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)). | +| [`useInVmCreationPermission`](#parameter-subnetoverridesuseinvmcreationpermission) | string | Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny). | +| [`usePublicIpAddressPermission`](#parameter-subnetoverridesusepublicipaddresspermission) | string | Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny). | +| [`virtualNetworkPoolName`](#parameter-subnetoverridesvirtualnetworkpoolname) | string | The virtual network pool associated with this subnet. | + +### Parameter: `subnetOverrides.labSubnetName` + +The name given to the subnet within the lab. + +- Required: Yes +- Type: string + +### Parameter: `subnetOverrides.resourceId` + +The resource ID of the subnet. + +- Required: Yes +- Type: string + +### Parameter: `subnetOverrides.sharedPublicIpAddressConfiguration` + +The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)). + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowedPorts`](#parameter-subnetoverridessharedpublicipaddressconfigurationallowedports) | array | Backend ports that virtual machines on this subnet are allowed to expose. | + +### Parameter: `subnetOverrides.sharedPublicIpAddressConfiguration.allowedPorts` + +Backend ports that virtual machines on this subnet are allowed to expose. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`backendPort`](#parameter-subnetoverridessharedpublicipaddressconfigurationallowedportsbackendport) | int | Backend port of the target virtual machine. | +| [`transportProtocol`](#parameter-subnetoverridessharedpublicipaddressconfigurationallowedportstransportprotocol) | string | Protocol type of the port. | + +### Parameter: `subnetOverrides.sharedPublicIpAddressConfiguration.allowedPorts.backendPort` + +Backend port of the target virtual machine. + +- Required: Yes +- Type: int + +### Parameter: `subnetOverrides.sharedPublicIpAddressConfiguration.allowedPorts.transportProtocol` + +Protocol type of the port. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Tcp' + 'Udp' + ] + ``` + +### Parameter: `subnetOverrides.useInVmCreationPermission` + +Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny). + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Allow' + 'Default' + 'Deny' + ] + ``` + +### Parameter: `subnetOverrides.usePublicIpAddressPermission` + +Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny). + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Allow' + 'Default' + 'Deny' + ] + ``` + +### Parameter: `subnetOverrides.virtualNetworkPoolName` + +The virtual network pool associated with this subnet. + +- Required: No +- Type: string ### Parameter: `tags` diff --git a/avm/res/dev-test-lab/lab/virtualnetwork/main.bicep b/avm/res/dev-test-lab/lab/virtualnetwork/main.bicep index 07e8f88432..ac6911125d 100644 --- a/avm/res/dev-test-lab/lab/virtualnetwork/main.bicep +++ b/avm/res/dev-test-lab/lab/virtualnetwork/main.bicep @@ -17,13 +17,13 @@ param externalProviderResourceId string param tags object? @sys.description('Optional. The description of the virtual network.') -param description string = '' +param description string? @sys.description('Optional. The allowed subnets of the virtual network.') -param allowedSubnets array = [] +param allowedSubnets allowedSubnetType @sys.description('Optional. The subnet overrides of the virtual network.') -param subnetOverrides array = [] +param subnetOverrides subnetOverrideType resource lab 'Microsoft.DevTestLab/labs@2018-09-15' existing = { name: labName @@ -64,7 +64,7 @@ type allowedSubnetType = { @sys.description('Required. The name of the subnet as seen in the lab.') labSubnetName: string -} +}[]? @export() type subnetOverrideType = { @@ -84,7 +84,7 @@ type subnetOverrideType = { @sys.description('Required. Protocol type of the port.') transportProtocol: 'Tcp' | 'Udp' }[] - } + }? @sys.description('Optional. Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny).') useInVmCreationPermission: 'Allow' | 'Deny' | 'Default'? @@ -94,4 +94,4 @@ type subnetOverrideType = { @sys.description('Optional. The virtual network pool associated with this subnet.') virtualNetworkPoolName: string? -} +}[]? diff --git a/avm/res/dev-test-lab/lab/virtualnetwork/main.json b/avm/res/dev-test-lab/lab/virtualnetwork/main.json index 1eab7d68eb..d68480b5a3 100644 --- a/avm/res/dev-test-lab/lab/virtualnetwork/main.json +++ b/avm/res/dev-test-lab/lab/virtualnetwork/main.json @@ -6,127 +6,136 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "18309816581107210302" + "templateHash": "12122718661184299591" }, "name": "DevTest Lab Virtual Networks", - "description": "This module deploys a DevTest Lab Virtual Network.\r\n\r\nLab virtual machines must be deployed into a virtual network. This resource type allows configuring the virtual network and subnet settings used for the lab virtual machines.", + "description": "This module deploys a DevTest Lab Virtual Network.\n\nLab virtual machines must be deployed into a virtual network. This resource type allows configuring the virtual network and subnet settings used for the lab virtual machines.", "owner": "Azure/module-maintainers" }, "definitions": { "allowedSubnetType": { - "type": "object", - "properties": { - "allowPublicIp": { - "type": "string", - "allowedValues": [ - "Allow", - "Default", - "Deny" - ], - "nullable": true, - "metadata": { - "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the allowed subnet." - } - }, - "labSubnetName": { - "type": "string", - "metadata": { - "description": "Required. The name of the subnet as seen in the lab." + "type": "array", + "items": { + "type": "object", + "properties": { + "allowPublicIp": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the allowed subnet." + } + }, + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name of the subnet as seen in the lab." + } } } }, + "nullable": true, "metadata": { "__bicep_export!": true } }, "subnetOverrideType": { - "type": "object", - "properties": { - "labSubnetName": { - "type": "string", - "metadata": { - "description": "Required. The name given to the subnet within the lab." - } - }, - "resourceId": { - "type": "string", - "metadata": { - "description": "Required. The resource ID of the subnet." - } - }, - "sharedPublicIpAddressConfiguration": { - "type": "object", - "properties": { - "allowedPorts": { - "type": "array", - "items": { - "type": "object", - "properties": { - "backendPort": { - "type": "int", - "metadata": { - "description": "Required. Backend port of the target virtual machine." - } - }, - "transportProtocol": { - "type": "string", - "allowedValues": [ - "Tcp", - "Udp" - ], - "metadata": { - "description": "Required. Protocol type of the port." + "type": "array", + "items": { + "type": "object", + "properties": { + "labSubnetName": { + "type": "string", + "metadata": { + "description": "Required. The name given to the subnet within the lab." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the subnet." + } + }, + "sharedPublicIpAddressConfiguration": { + "type": "object", + "properties": { + "allowedPorts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "backendPort": { + "type": "int", + "metadata": { + "description": "Required. Backend port of the target virtual machine." + } + }, + "transportProtocol": { + "type": "string", + "allowedValues": [ + "Tcp", + "Udp" + ], + "metadata": { + "description": "Required. Protocol type of the port." + } } } + }, + "metadata": { + "description": "Required. Backend ports that virtual machines on this subnet are allowed to expose." } - }, - "metadata": { - "description": "Required. Backend ports that virtual machines on this subnet are allowed to expose." } + }, + "nullable": true, + "metadata": { + "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." } }, - "metadata": { - "description": "Optional. The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny))." - } - }, - "useInVmCreationPermission": { - "type": "string", - "allowedValues": [ - "Allow", - "Default", - "Deny" - ], - "nullable": true, - "metadata": { - "description": "Optional. Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny)." - } - }, - "usePublicIpAddressPermission": { - "type": "string", - "allowedValues": [ - "Allow", - "Default", - "Deny" - ], - "nullable": true, - "metadata": { - "description": "Optional. Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny)." - } - }, - "virtualNetworkPoolName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The virtual network pool associated with this subnet." + "useInVmCreationPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether this subnet can be used during virtual machine creation (i.e. Allow, Deny)." + } + }, + "usePublicIpAddressPermission": { + "type": "string", + "allowedValues": [ + "Allow", + "Default", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Indicates whether public IP addresses can be assigned to virtual machines on this subnet (i.e. Allow, Deny)." + } + }, + "virtualNetworkPoolName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The virtual network pool associated with this subnet." + } } } }, + "nullable": true, "metadata": { "__bicep_export!": true } @@ -160,21 +169,19 @@ }, "description": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The description of the virtual network." } }, "allowedSubnets": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/allowedSubnetType", "metadata": { "description": "Optional. The allowed subnets of the virtual network." } }, "subnetOverrides": { - "type": "array", - "defaultValue": [], + "$ref": "#/definitions/subnetOverrideType", "metadata": { "description": "Optional. The subnet overrides of the virtual network." } From d13be75d4ee0012933cc638f7a170154121c3108 Mon Sep 17 00:00:00 2001 From: Kris Baranek <20225789+krbar@users.noreply.github.com> Date: Mon, 9 Sep 2024 09:09:40 +0200 Subject: [PATCH 07/57] fix: Remove '(Preview)' label from role name - batch 4 (#3229) ## Description Remove '(Preview)' label from all 'Role Based Access Control Administrator' role as described in #3112 ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.authorization.role-assignment](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.ptn.authorization.role-assignment.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.ptn.authorization.role-assignment.yml) | | [![avm.res.operational-insights.workspace](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.operational-insights.workspace.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.operational-insights.workspace.yml) | | [![avm.res.portal.dashboard](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.portal.dashboard.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.portal.dashboard.yml) | | [![avm.res.purview.account](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.purview.account.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.purview.account.yml) | | [![avm.res.recovery-services.vault](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.recovery-services.vault.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.recovery-services.vault.yml) | | [![avm.res.relay.namespace](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.relay.namespace.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.relay.namespace.yml) | | [![avm.res.resource-graph.query](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.resource-graph.query.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.resource-graph.query.yml) | | [![avm.res.resources.deployment-script](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.resources.deployment-script.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.resources.deployment-script.yml) | | [![avm.res.resources.resource-group](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.resources.resource-group.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.resources.resource-group.yml) | | [![avm.res.search.search-service](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.search.search-service.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.search.search-service.yml) | | [![avm.res.service-bus.namespace](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.service-bus.namespace.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.service-bus.namespace.yml) | | [![avm.res.service-fabric.cluster](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.service-fabric.cluster.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.service-fabric.cluster.yml) | | [![avm.res.signal-r-service.signal-r](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.signal-r.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.signal-r.yml) | | [![avm.res.signal-r-service.web-pub-sub](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.web-pub-sub.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.web-pub-sub.yml) | | [![avm.res.sql.server](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.sql.server.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.sql.server.yml) | | [![avm.res.storage.storage-account](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml) | | [![avm.res.synapse.private-link-hub](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.synapse.private-link-hub.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.synapse.private-link-hub.yml) | | [![avm.res.synapse.workspace](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.synapse.workspace.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.synapse.workspace.yml) | | [![avm.res.virtual-machine-images.image-template](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.virtual-machine-images.image-template.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.virtual-machine-images.image-template.yml) | | [![avm.res.web.connection](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.web.connection.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.web.connection.yml) | | [![avm.res.web.site](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.web.site.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.web.site.yml) | | [![avm.res.web.static-site](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.web.static-site.yml/badge.svg?branch=users%2Fkrbar%2FrbacAdminRename4&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.web.static-site.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [x] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../authorization/role-assignment/main.json | 22 +++++++------- .../modules/management-group.bicep | 24 +++++++++++---- .../modules/resource-group.bicep | 18 ++++++++--- .../modules/subscription.bicep | 18 ++++++++--- .../role-assignment/version.json | 4 +-- .../operational-insights/workspace/main.bicep | 2 +- .../operational-insights/workspace/main.json | 8 ++--- .../workspace/table/main.bicep | 2 +- .../workspace/table/main.json | 4 +-- .../workspace/version.json | 2 +- avm/res/portal/dashboard/main.bicep | 2 +- avm/res/portal/dashboard/main.json | 4 +-- avm/res/portal/dashboard/version.json | 2 +- avm/res/purview/account/main.bicep | 2 +- avm/res/purview/account/main.json | 4 +-- avm/res/purview/account/version.json | 2 +- avm/res/recovery-services/vault/main.bicep | 2 +- avm/res/recovery-services/vault/main.json | 4 +-- avm/res/recovery-services/vault/version.json | 2 +- .../namespace/hybrid-connection/main.bicep | 2 +- .../namespace/hybrid-connection/main.json | 4 +-- avm/res/relay/namespace/main.bicep | 2 +- avm/res/relay/namespace/main.json | 12 ++++---- avm/res/relay/namespace/version.json | 2 +- avm/res/relay/namespace/wcf-relay/main.bicep | 2 +- avm/res/relay/namespace/wcf-relay/main.json | 4 +-- avm/res/resource-graph/query/main.bicep | 2 +- avm/res/resource-graph/query/main.json | 4 +-- avm/res/resource-graph/query/version.json | 2 +- .../resources/deployment-script/main.bicep | 2 +- avm/res/resources/deployment-script/main.json | 4 +-- .../resources/deployment-script/version.json | 2 +- avm/res/resources/resource-group/main.json | 6 ++-- .../modules/nested_roleAssignments.bicep | 2 +- avm/res/resources/resource-group/version.json | 2 +- avm/res/search/search-service/main.bicep | 2 +- avm/res/search/search-service/main.json | 4 +-- avm/res/search/search-service/version.json | 2 +- avm/res/service-bus/namespace/main.bicep | 2 +- avm/res/service-bus/namespace/main.json | 12 ++++---- .../service-bus/namespace/queue/main.bicep | 2 +- avm/res/service-bus/namespace/queue/main.json | 4 +-- .../service-bus/namespace/topic/main.bicep | 2 +- avm/res/service-bus/namespace/topic/main.json | 4 +-- avm/res/service-bus/namespace/version.json | 2 +- avm/res/service-fabric/cluster/main.bicep | 2 +- avm/res/service-fabric/cluster/main.json | 4 +-- avm/res/service-fabric/cluster/version.json | 2 +- avm/res/signal-r-service/signal-r/main.bicep | 2 +- avm/res/signal-r-service/signal-r/main.json | 4 +-- .../signal-r-service/signal-r/version.json | 2 +- .../signal-r-service/web-pub-sub/main.bicep | 2 +- .../signal-r-service/web-pub-sub/main.json | 4 +-- .../signal-r-service/web-pub-sub/version.json | 2 +- avm/res/sql/server/main.bicep | 2 +- avm/res/sql/server/main.json | 4 +-- avm/res/sql/server/version.json | 2 +- .../blob-service/container/main.bicep | 2 +- .../blob-service/container/main.json | 4 +-- .../storage-account/blob-service/main.json | 6 ++-- .../storage-account/file-service/main.json | 8 ++--- .../file-service/share/main.json | 6 ++-- .../share/modules/nested_roleAssignment.bicep | 2 +- avm/res/storage/storage-account/main.bicep | 2 +- avm/res/storage/storage-account/main.json | 30 +++++++++---------- .../storage-account/queue-service/main.json | 6 ++-- .../queue-service/queue/main.bicep | 2 +- .../queue-service/queue/main.json | 4 +-- .../storage-account/table-service/main.json | 6 ++-- .../table-service/table/main.bicep | 2 +- .../table-service/table/main.json | 4 +-- avm/res/storage/storage-account/version.json | 2 +- avm/res/synapse/private-link-hub/main.bicep | 2 +- avm/res/synapse/private-link-hub/main.json | 4 +-- avm/res/synapse/private-link-hub/version.json | 2 +- avm/res/synapse/workspace/main.bicep | 2 +- avm/res/synapse/workspace/main.json | 4 +-- avm/res/synapse/workspace/version.json | 2 +- .../image-template/main.bicep | 2 +- .../image-template/main.json | 4 +-- .../image-template/version.json | 2 +- avm/res/web/connection/main.bicep | 2 +- avm/res/web/connection/main.json | 4 +-- avm/res/web/connection/version.json | 2 +- avm/res/web/site/main.bicep | 2 +- avm/res/web/site/main.json | 8 ++--- avm/res/web/site/slot/main.bicep | 2 +- avm/res/web/site/slot/main.json | 4 +-- avm/res/web/site/version.json | 2 +- avm/res/web/static-site/main.bicep | 2 +- avm/res/web/static-site/main.json | 4 +-- avm/res/web/static-site/version.json | 2 +- 92 files changed, 213 insertions(+), 181 deletions(-) diff --git a/avm/ptn/authorization/role-assignment/main.json b/avm/ptn/authorization/role-assignment/main.json index e7d405fa2b..e908d925ce 100644 --- a/avm/ptn/authorization/role-assignment/main.json +++ b/avm/ptn/authorization/role-assignment/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "7625867886550880091" + "version": "0.29.47.4906", + "templateHash": "11572179981104603818" }, "name": "Role Assignments (All scopes)", "description": "This module deploys a Role Assignment at a Management Group, Subscription or Resource Group scope.", @@ -164,8 +164,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "8906093264527258150" + "version": "0.29.47.4906", + "templateHash": "16307162966269052514" }, "name": "Role Assignments (Management Group scope)", "description": "This module deploys a Role Assignment at a Management Group scope.", @@ -244,7 +244,7 @@ "Owner": "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Resource Policy Contributor": "/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", "Management Group Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ac63b705-f282-497d-ac71-919bf39d939d')]" }, @@ -328,8 +328,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "707244099707019442" + "version": "0.29.47.4906", + "templateHash": "7865292958983636483" }, "name": "Role Assignments (Subscription scope)", "description": "This module deploys a Role Assignment at a Subscription scope.", @@ -407,7 +407,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" }, "roleDefinitionIdVar": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]" @@ -500,8 +500,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "14591941439222880522" + "version": "0.29.47.4906", + "templateHash": "4013817963006824674" }, "name": "Role Assignments (Resource Group scope)", "description": "This module deploys a Role Assignment at a Resource Group scope.", @@ -585,7 +585,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" }, "roleDefinitionIdVar": "[if(contains(variables('builtInRoleNames'), parameters('roleDefinitionIdOrName')), variables('builtInRoleNames')[parameters('roleDefinitionIdOrName')], parameters('roleDefinitionIdOrName'))]" diff --git a/avm/ptn/authorization/role-assignment/modules/management-group.bicep b/avm/ptn/authorization/role-assignment/modules/management-group.bicep index b1a51d40f9..18381b16e1 100644 --- a/avm/ptn/authorization/role-assignment/modules/management-group.bicep +++ b/avm/ptn/authorization/role-assignment/modules/management-group.bicep @@ -44,13 +44,23 @@ var builtInRoleNames = { Owner: '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635' Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') 'Resource Policy Contributor': '/providers/Microsoft.Authorization/roleDefinitions/36243c78-bf99-498c-9df9-86d9f8d28608' - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') - 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') - 'Management Group Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ac63b705-f282-497d-ac71-919bf39d939d') + 'Role Based Access Control Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) + 'Management Group Reader': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'ac63b705-f282-497d-ac71-919bf39d939d' + ) } -var roleDefinitionIdVar = (contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName) - +var roleDefinitionIdVar = (contains(builtInRoleNames, roleDefinitionIdOrName) + ? builtInRoleNames[roleDefinitionIdOrName] + : roleDefinitionIdOrName) resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(managementGroupId, roleDefinitionIdVar, principalId) @@ -59,7 +69,9 @@ resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { principalId: principalId description: !empty(description) ? description : null principalType: !empty(principalType) ? any(principalType) : null - delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) + ? delegatedManagedIdentityResourceId + : null conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null condition: !empty(condition) ? condition : null } diff --git a/avm/ptn/authorization/role-assignment/modules/resource-group.bicep b/avm/ptn/authorization/role-assignment/modules/resource-group.bicep index 459bde3f99..edf04b698a 100644 --- a/avm/ptn/authorization/role-assignment/modules/resource-group.bicep +++ b/avm/ptn/authorization/role-assignment/modules/resource-group.bicep @@ -46,11 +46,19 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') - 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Role Based Access Control Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) } -var roleDefinitionIdVar = (contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName) +var roleDefinitionIdVar = (contains(builtInRoleNames, roleDefinitionIdOrName) + ? builtInRoleNames[roleDefinitionIdOrName] + : roleDefinitionIdOrName) resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(subscriptionId, resourceGroupName, roleDefinitionIdVar, principalId) @@ -59,7 +67,9 @@ resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { principalId: principalId description: !empty(description) ? description : null principalType: !empty(principalType) ? any(principalType) : null - delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) + ? delegatedManagedIdentityResourceId + : null conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null condition: !empty(condition) ? condition : null } diff --git a/avm/ptn/authorization/role-assignment/modules/subscription.bicep b/avm/ptn/authorization/role-assignment/modules/subscription.bicep index 2a41bc6b07..c74f7eb513 100644 --- a/avm/ptn/authorization/role-assignment/modules/subscription.bicep +++ b/avm/ptn/authorization/role-assignment/modules/subscription.bicep @@ -43,11 +43,19 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168') - 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Role Based Access Control Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) } -var roleDefinitionIdVar = (contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName) +var roleDefinitionIdVar = (contains(builtInRoleNames, roleDefinitionIdOrName) + ? builtInRoleNames[roleDefinitionIdOrName] + : roleDefinitionIdOrName) resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid(subscriptionId, roleDefinitionIdVar, principalId) @@ -56,7 +64,9 @@ resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { principalId: principalId description: !empty(description) ? description : null principalType: !empty(principalType) ? any(principalType) : null - delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) ? delegatedManagedIdentityResourceId : null + delegatedManagedIdentityResourceId: !empty(delegatedManagedIdentityResourceId) + ? delegatedManagedIdentityResourceId + : null conditionVersion: !empty(conditionVersion) && !empty(condition) ? conditionVersion : null condition: !empty(condition) ? condition : null } diff --git a/avm/ptn/authorization/role-assignment/version.json b/avm/ptn/authorization/role-assignment/version.json index 7fa401bdf7..1c035df49f 100644 --- a/avm/ptn/authorization/role-assignment/version.json +++ b/avm/ptn/authorization/role-assignment/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file diff --git a/avm/res/operational-insights/workspace/main.bicep b/avm/res/operational-insights/workspace/main.bicep index 4c6bd5e5a1..b5be253202 100644 --- a/avm/res/operational-insights/workspace/main.bicep +++ b/avm/res/operational-insights/workspace/main.bicep @@ -132,7 +132,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/operational-insights/workspace/main.json b/avm/res/operational-insights/workspace/main.json index ccbd83e3c1..61d489e40d 100644 --- a/avm/res/operational-insights/workspace/main.json +++ b/avm/res/operational-insights/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11994891550110156380" + "templateHash": "15059379956726052447" }, "name": "Log Analytics Workspaces", "description": "This module deploys a Log Analytics Workspace.", @@ -468,7 +468,7 @@ "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')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[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')]" @@ -1567,7 +1567,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "6905244456918791391" + "templateHash": "10380077652898392916" }, "name": "Log Analytics Workspace Tables", "description": "This module deploys a Log Analytics Workspace Table.", @@ -1734,7 +1734,7 @@ "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')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/operational-insights/workspace/table/main.bicep b/avm/res/operational-insights/workspace/table/main.bicep index cee095b8d2..b38ff9210f 100644 --- a/avm/res/operational-insights/workspace/table/main.bicep +++ b/avm/res/operational-insights/workspace/table/main.bicep @@ -61,7 +61,7 @@ var builtInRoleNames = { ) Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/operational-insights/workspace/table/main.json b/avm/res/operational-insights/workspace/table/main.json index 18cdd3e09b..6a1e1e11a6 100644 --- a/avm/res/operational-insights/workspace/table/main.json +++ b/avm/res/operational-insights/workspace/table/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "6905244456918791391" + "templateHash": "10380077652898392916" }, "name": "Log Analytics Workspace Tables", "description": "This module deploys a Log Analytics Workspace Table.", @@ -173,7 +173,7 @@ "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')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/operational-insights/workspace/version.json b/avm/res/operational-insights/workspace/version.json index e42c3d9e5f..7e1d3f4157 100644 --- a/avm/res/operational-insights/workspace/version.json +++ b/avm/res/operational-insights/workspace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.6", + "version": "0.7", "pathFilters": [ "./main.json" ] diff --git a/avm/res/portal/dashboard/main.bicep b/avm/res/portal/dashboard/main.bicep index b732ee714c..aa4b03c4de 100644 --- a/avm/res/portal/dashboard/main.bicep +++ b/avm/res/portal/dashboard/main.bicep @@ -31,7 +31,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/portal/dashboard/main.json b/avm/res/portal/dashboard/main.json index 32fcb39e93..b8eca11f50 100644 --- a/avm/res/portal/dashboard/main.json +++ b/avm/res/portal/dashboard/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4329413484700617362" + "templateHash": "417422598154649479" }, "name": "Portal Dashboards", "description": "This module deploys a Portal Dashboard.", @@ -182,7 +182,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/portal/dashboard/version.json b/avm/res/portal/dashboard/version.json index 729ac87673..76049e1c4a 100644 --- a/avm/res/portal/dashboard/version.json +++ b/avm/res/portal/dashboard/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/purview/account/main.bicep b/avm/res/purview/account/main.bicep index 392942cbf6..e0d408fdb1 100644 --- a/avm/res/purview/account/main.bicep +++ b/avm/res/purview/account/main.bicep @@ -73,7 +73,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/purview/account/main.json b/avm/res/purview/account/main.json index f4cf2712e1..34bd78df71 100644 --- a/avm/res/purview/account/main.json +++ b/avm/res/purview/account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "15039051778035234501" + "templateHash": "14495497067965615328" }, "name": "Purview Accounts", "description": "This module deploys a Purview Account.", @@ -588,7 +588,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/purview/account/version.json b/avm/res/purview/account/version.json index 3f863a2bec..a8eda31021 100644 --- a/avm/res/purview/account/version.json +++ b/avm/res/purview/account/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/recovery-services/vault/main.bicep b/avm/res/recovery-services/vault/main.bicep index 5ee99722fa..6f984370b8 100644 --- a/avm/res/recovery-services/vault/main.bicep +++ b/avm/res/recovery-services/vault/main.bicep @@ -94,7 +94,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/recovery-services/vault/main.json b/avm/res/recovery-services/vault/main.json index 91ffe8c500..f05335967c 100644 --- a/avm/res/recovery-services/vault/main.json +++ b/avm/res/recovery-services/vault/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "1317340221517130136" + "templateHash": "11169688111946598929" }, "name": "Recovery Services Vaults", "description": "This module deploys a Recovery Services Vault.", @@ -625,7 +625,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[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')]", "Site Recovery Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'dbaa88c4-0c30-4179-9fb3-46319faa6149')]", diff --git a/avm/res/recovery-services/vault/version.json b/avm/res/recovery-services/vault/version.json index 3f863a2bec..a8eda31021 100644 --- a/avm/res/recovery-services/vault/version.json +++ b/avm/res/recovery-services/vault/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/relay/namespace/hybrid-connection/main.bicep b/avm/res/relay/namespace/hybrid-connection/main.bicep index 6c31cfede6..e12ee555a9 100644 --- a/avm/res/relay/namespace/hybrid-connection/main.bicep +++ b/avm/res/relay/namespace/hybrid-connection/main.bicep @@ -64,7 +64,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/relay/namespace/hybrid-connection/main.json b/avm/res/relay/namespace/hybrid-connection/main.json index 219af4e2d5..b5807ecc0d 100644 --- a/avm/res/relay/namespace/hybrid-connection/main.json +++ b/avm/res/relay/namespace/hybrid-connection/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "6090434662270085474" + "templateHash": "3066452421480890151" }, "name": "Relay Namespace Hybrid Connections", "description": "This module deploys a Relay Namespace Hybrid Connection.", @@ -198,7 +198,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/relay/namespace/main.bicep b/avm/res/relay/namespace/main.bicep index e28a037e51..9758646370 100644 --- a/avm/res/relay/namespace/main.bicep +++ b/avm/res/relay/namespace/main.bicep @@ -71,7 +71,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/relay/namespace/main.json b/avm/res/relay/namespace/main.json index 66c0fbf16e..dab21e582d 100644 --- a/avm/res/relay/namespace/main.json +++ b/avm/res/relay/namespace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "10535689791446770792" + "templateHash": "10748276001217770184" }, "name": "Relay Namespaces", "description": "This module deploys a Relay Namespace", @@ -569,7 +569,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -939,7 +939,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "6090434662270085474" + "templateHash": "3066452421480890151" }, "name": "Relay Namespace Hybrid Connections", "description": "This module deploys a Relay Namespace Hybrid Connection.", @@ -1131,7 +1131,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -1367,7 +1367,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4256310187824299728" + "templateHash": "25032660008872161" }, "name": "Relay Namespace WCF Relays", "description": "This module deploys a Relay Namespace WCF Relay.", @@ -1577,7 +1577,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/relay/namespace/version.json b/avm/res/relay/namespace/version.json index 3f863a2bec..a8eda31021 100644 --- a/avm/res/relay/namespace/version.json +++ b/avm/res/relay/namespace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/relay/namespace/wcf-relay/main.bicep b/avm/res/relay/namespace/wcf-relay/main.bicep index d38ae8a614..c2d9814469 100644 --- a/avm/res/relay/namespace/wcf-relay/main.bicep +++ b/avm/res/relay/namespace/wcf-relay/main.bicep @@ -74,7 +74,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/relay/namespace/wcf-relay/main.json b/avm/res/relay/namespace/wcf-relay/main.json index f5db122683..681b9dac73 100644 --- a/avm/res/relay/namespace/wcf-relay/main.json +++ b/avm/res/relay/namespace/wcf-relay/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4256310187824299728" + "templateHash": "25032660008872161" }, "name": "Relay Namespace WCF Relays", "description": "This module deploys a Relay Namespace WCF Relay.", @@ -216,7 +216,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/resource-graph/query/main.bicep b/avm/res/resource-graph/query/main.bicep index 3f02f3f7ee..80149b6fc4 100644 --- a/avm/res/resource-graph/query/main.bicep +++ b/avm/res/resource-graph/query/main.bicep @@ -38,7 +38,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/resource-graph/query/main.json b/avm/res/resource-graph/query/main.json index 9ba837cb40..f11a8d7c87 100644 --- a/avm/res/resource-graph/query/main.json +++ b/avm/res/resource-graph/query/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "2991554121224170850" + "templateHash": "15439682235813635403" }, "name": "Resource Graph Queries", "description": "This module deploys a Resource Graph Query.", @@ -178,7 +178,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/resource-graph/query/version.json b/avm/res/resource-graph/query/version.json index 1c035df49f..c177b1bb58 100644 --- a/avm/res/resource-graph/query/version.json +++ b/avm/res/resource-graph/query/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] diff --git a/avm/res/resources/deployment-script/main.bicep b/avm/res/resources/deployment-script/main.bicep index 55dc0612e2..49b96d7a48 100644 --- a/avm/res/resources/deployment-script/main.bicep +++ b/avm/res/resources/deployment-script/main.bicep @@ -92,7 +92,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/resources/deployment-script/main.json b/avm/res/resources/deployment-script/main.json index 96f10eba58..37eaaa6a2a 100644 --- a/avm/res/resources/deployment-script/main.json +++ b/avm/res/resources/deployment-script/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11095024633645020544" + "templateHash": "8443498654175834102" }, "name": "Deployment Scripts", "description": "This module deploys Deployment Scripts.", @@ -345,7 +345,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" }, "containerSettings": { diff --git a/avm/res/resources/deployment-script/version.json b/avm/res/resources/deployment-script/version.json index 76049e1c4a..13669e6601 100644 --- a/avm/res/resources/deployment-script/version.json +++ b/avm/res/resources/deployment-script/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/resources/resource-group/main.json b/avm/res/resources/resource-group/main.json index d0a5b4e2a3..3e371b3059 100644 --- a/avm/res/resources/resource-group/main.json +++ b/avm/res/resources/resource-group/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "1112152378548482375" + "templateHash": "12280129054844550830" }, "name": "Resource Groups", "description": "This module deploys a Resource Group.", @@ -296,7 +296,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4236428492292165426" + "templateHash": "10992477577200576081" } }, "definitions": { @@ -396,7 +396,7 @@ "Quota Request Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0e5f05e5-9ab9-446b-b98d-1e2157c94125')]", "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')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "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')]", diff --git a/avm/res/resources/resource-group/modules/nested_roleAssignments.bicep b/avm/res/resources/resource-group/modules/nested_roleAssignments.bicep index bf8bc903d4..9916ca8032 100644 --- a/avm/res/resources/resource-group/modules/nested_roleAssignments.bicep +++ b/avm/res/resources/resource-group/modules/nested_roleAssignments.bicep @@ -13,7 +13,7 @@ var builtInRoleNames = { 'Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608' ) - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/resources/resource-group/version.json b/avm/res/resources/resource-group/version.json index c177b1bb58..3f863a2bec 100644 --- a/avm/res/resources/resource-group/version.json +++ b/avm/res/resources/resource-group/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/search/search-service/main.bicep b/avm/res/search/search-service/main.bicep index bc455a8f68..951adfe725 100644 --- a/avm/res/search/search-service/main.bicep +++ b/avm/res/search/search-service/main.bicep @@ -123,7 +123,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/search/search-service/main.json b/avm/res/search/search-service/main.json index 04beacbf9e..c4b625ff47 100644 --- a/avm/res/search/search-service/main.json +++ b/avm/res/search/search-service/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "10051405020131908010" + "templateHash": "8483667347070963331" }, "name": "Search Services", "description": "This module deploys a Search Service.", @@ -656,7 +656,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "Search Index Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8ebe5a00-799e-43f5-93ac-243d3dce84a7')]", "Search Index Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1407120a-92aa-4202-b7e9-c0e197c71c8f')]", "Search Service Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7ca78c08-252a-4471-8644-bb5ff32d4ba0')]", diff --git a/avm/res/search/search-service/version.json b/avm/res/search/search-service/version.json index e42c3d9e5f..7e1d3f4157 100644 --- a/avm/res/search/search-service/version.json +++ b/avm/res/search/search-service/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.6", + "version": "0.7", "pathFilters": [ "./main.json" ] diff --git a/avm/res/service-bus/namespace/main.bicep b/avm/res/service-bus/namespace/main.bicep index 507297992b..08ecf97e72 100644 --- a/avm/res/service-bus/namespace/main.bicep +++ b/avm/res/service-bus/namespace/main.bicep @@ -126,7 +126,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/service-bus/namespace/main.json b/avm/res/service-bus/namespace/main.json index e3bdecdc0f..b3917916f0 100644 --- a/avm/res/service-bus/namespace/main.json +++ b/avm/res/service-bus/namespace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "10964684048264975988" + "templateHash": "16628890374295506516" }, "name": "Service Bus Namespaces", "description": "This module deploys a Service Bus Namespace.", @@ -1318,7 +1318,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -2020,7 +2020,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "7247345844358420118" + "templateHash": "12442268068778335924" }, "name": "Service Bus Namespace Queue", "description": "This module deploys a Service Bus Namespace Queue.", @@ -2301,7 +2301,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -2589,7 +2589,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "3425340822225922607" + "templateHash": "782028791267114581" }, "name": "Service Bus Namespace Topic", "description": "This module deploys a Service Bus Namespace Topic.", @@ -2998,7 +2998,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/service-bus/namespace/queue/main.bicep b/avm/res/service-bus/namespace/queue/main.bicep index c8375f00a1..b6e1097705 100644 --- a/avm/res/service-bus/namespace/queue/main.bicep +++ b/avm/res/service-bus/namespace/queue/main.bicep @@ -96,7 +96,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/service-bus/namespace/queue/main.json b/avm/res/service-bus/namespace/queue/main.json index 7eb7008009..49ba95b068 100644 --- a/avm/res/service-bus/namespace/queue/main.json +++ b/avm/res/service-bus/namespace/queue/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "7247345844358420118" + "templateHash": "12442268068778335924" }, "name": "Service Bus Namespace Queue", "description": "This module deploys a Service Bus Namespace Queue.", @@ -287,7 +287,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/service-bus/namespace/topic/main.bicep b/avm/res/service-bus/namespace/topic/main.bicep index 9b4683fb7c..2009633393 100644 --- a/avm/res/service-bus/namespace/topic/main.bicep +++ b/avm/res/service-bus/namespace/topic/main.bicep @@ -95,7 +95,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/service-bus/namespace/topic/main.json b/avm/res/service-bus/namespace/topic/main.json index 55bcfc9651..f037f5762d 100644 --- a/avm/res/service-bus/namespace/topic/main.json +++ b/avm/res/service-bus/namespace/topic/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "3425340822225922607" + "templateHash": "782028791267114581" }, "name": "Service Bus Namespace Topic", "description": "This module deploys a Service Bus Namespace Topic.", @@ -415,7 +415,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/service-bus/namespace/version.json b/avm/res/service-bus/namespace/version.json index 09c3664cec..9a9a06e897 100644 --- a/avm/res/service-bus/namespace/version.json +++ b/avm/res/service-bus/namespace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.7", + "version": "0.8", "pathFilters": [ "./main.json" ] diff --git a/avm/res/service-fabric/cluster/main.bicep b/avm/res/service-fabric/cluster/main.bicep index cef122c80f..41089bc7ac 100644 --- a/avm/res/service-fabric/cluster/main.bicep +++ b/avm/res/service-fabric/cluster/main.bicep @@ -246,7 +246,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/service-fabric/cluster/main.json b/avm/res/service-fabric/cluster/main.json index ae343dc117..3f0955fb78 100644 --- a/avm/res/service-fabric/cluster/main.json +++ b/avm/res/service-fabric/cluster/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "10632901635628178846" + "templateHash": "11160466068108868601" }, "name": "Service Fabric Clusters", "description": "This module deploys a Service Fabric Cluster.", @@ -572,7 +572,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/service-fabric/cluster/version.json b/avm/res/service-fabric/cluster/version.json index 76049e1c4a..13669e6601 100644 --- a/avm/res/service-fabric/cluster/version.json +++ b/avm/res/service-fabric/cluster/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/signal-r-service/signal-r/main.bicep b/avm/res/signal-r-service/signal-r/main.bicep index 37b7449350..ece400d2ed 100644 --- a/avm/res/signal-r-service/signal-r/main.bicep +++ b/avm/res/signal-r-service/signal-r/main.bicep @@ -128,7 +128,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/signal-r-service/signal-r/main.json b/avm/res/signal-r-service/signal-r/main.json index 6ed88a6fad..b6f8656b92 100644 --- a/avm/res/signal-r-service/signal-r/main.json +++ b/avm/res/signal-r-service/signal-r/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "6232258125767604001" + "templateHash": "16576967735793916107" }, "name": "SignalR Service SignalR", "description": "This module deploys a SignalR Service SignalR.", @@ -548,7 +548,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "SignalR AccessKey Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '04165923-9d83-45d5-8227-78b77b0a687e')]", "SignalR App Server": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '420fcaa2-552c-430f-98ca-3264be4806c7')]", "SignalR REST API Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fd53cd77-2268-407a-8f46-7e7863d0f521')]", diff --git a/avm/res/signal-r-service/signal-r/version.json b/avm/res/signal-r-service/signal-r/version.json index 3f863a2bec..a8eda31021 100644 --- a/avm/res/signal-r-service/signal-r/version.json +++ b/avm/res/signal-r-service/signal-r/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/signal-r-service/web-pub-sub/main.bicep b/avm/res/signal-r-service/web-pub-sub/main.bicep index a3821042f8..760511f84b 100644 --- a/avm/res/signal-r-service/web-pub-sub/main.bicep +++ b/avm/res/signal-r-service/web-pub-sub/main.bicep @@ -91,7 +91,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/signal-r-service/web-pub-sub/main.json b/avm/res/signal-r-service/web-pub-sub/main.json index 5807cd4da1..51dcc979c6 100644 --- a/avm/res/signal-r-service/web-pub-sub/main.json +++ b/avm/res/signal-r-service/web-pub-sub/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "3619836062547426326" + "templateHash": "17348552013839664242" }, "name": "SignalR Web PubSub Services", "description": "This module deploys a SignalR Web PubSub Service.", @@ -501,7 +501,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "SignalR AccessKey Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '04165923-9d83-45d5-8227-78b77b0a687e')]", "SignalR App Server": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '420fcaa2-552c-430f-98ca-3264be4806c7')]", "SignalR REST API Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fd53cd77-2268-407a-8f46-7e7863d0f521')]", diff --git a/avm/res/signal-r-service/web-pub-sub/version.json b/avm/res/signal-r-service/web-pub-sub/version.json index 3f863a2bec..a8eda31021 100644 --- a/avm/res/signal-r-service/web-pub-sub/version.json +++ b/avm/res/signal-r-service/web-pub-sub/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.5", "pathFilters": [ "./main.json" ] diff --git a/avm/res/sql/server/main.bicep b/avm/res/sql/server/main.bicep index 253c26b244..e628668146 100644 --- a/avm/res/sql/server/main.bicep +++ b/avm/res/sql/server/main.bicep @@ -113,7 +113,7 @@ var builtInRoleNames = { 'Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689' ) - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/sql/server/main.json b/avm/res/sql/server/main.json index 6f055afb2c..b13a78c08f 100644 --- a/avm/res/sql/server/main.json +++ b/avm/res/sql/server/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "5025800136020814452" + "templateHash": "13031224188572751832" }, "name": "Azure SQL Servers", "description": "This module deploys an Azure SQL Server.", @@ -631,7 +631,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Reservation Purchaser": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f7b75c60-3036-4b75-91c3-6b41c27c1689')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "SQL DB Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9b7fa17d-e63e-47b0-bb0a-15c516ac86ec')]", "SQL Managed Instance Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4939a1f6-9ae0-4e48-a1e0-f2cbe897382d')]", "SQL Security Manager": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '056cd41c-7e88-42e1-933e-88ba6a50c9c3')]", diff --git a/avm/res/sql/server/version.json b/avm/res/sql/server/version.json index 7e1d3f4157..0f81d22abc 100644 --- a/avm/res/sql/server/version.json +++ b/avm/res/sql/server/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.7", + "version": "0.8", "pathFilters": [ "./main.json" ] diff --git a/avm/res/storage/storage-account/blob-service/container/main.bicep b/avm/res/storage/storage-account/blob-service/container/main.bicep index fd1c851da3..fa0193da72 100644 --- a/avm/res/storage/storage-account/blob-service/container/main.bicep +++ b/avm/res/storage/storage-account/blob-service/container/main.bicep @@ -52,7 +52,7 @@ var builtInRoleNames = { 'Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349' ) - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/storage/storage-account/blob-service/container/main.json b/avm/res/storage/storage-account/blob-service/container/main.json index c2b8aebe7d..98d00e679f 100644 --- a/avm/res/storage/storage-account/blob-service/container/main.json +++ b/avm/res/storage/storage-account/blob-service/container/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "2376441074312126168" + "templateHash": "1020003258393866601" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -189,7 +189,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "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')]", "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", diff --git a/avm/res/storage/storage-account/blob-service/main.json b/avm/res/storage/storage-account/blob-service/main.json index 2a053636e7..7531267468 100644 --- a/avm/res/storage/storage-account/blob-service/main.json +++ b/avm/res/storage/storage-account/blob-service/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "6152214765227449988" + "templateHash": "17077763197163073998" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service.", @@ -404,7 +404,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "2376441074312126168" + "templateHash": "1020003258393866601" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -587,7 +587,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "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')]", "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", diff --git a/avm/res/storage/storage-account/file-service/main.json b/avm/res/storage/storage-account/file-service/main.json index 4e9d267c82..9375230d2f 100644 --- a/avm/res/storage/storage-account/file-service/main.json +++ b/avm/res/storage/storage-account/file-service/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11112322068652007329" + "templateHash": "1933197013743223154" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service.", @@ -287,7 +287,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "7774942141784896900" + "templateHash": "13477688809575027800" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share.", @@ -494,7 +494,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "17818484349715313082" + "templateHash": "10820882302387746924" } }, "parameters": { @@ -617,7 +617,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "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')]", "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", diff --git a/avm/res/storage/storage-account/file-service/share/main.json b/avm/res/storage/storage-account/file-service/share/main.json index 0d4365a4a4..90dc220560 100644 --- a/avm/res/storage/storage-account/file-service/share/main.json +++ b/avm/res/storage/storage-account/file-service/share/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "7774942141784896900" + "templateHash": "13477688809575027800" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share.", @@ -213,7 +213,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "17818484349715313082" + "templateHash": "10820882302387746924" } }, "parameters": { @@ -336,7 +336,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "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')]", "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", diff --git a/avm/res/storage/storage-account/file-service/share/modules/nested_roleAssignment.bicep b/avm/res/storage/storage-account/file-service/share/modules/nested_roleAssignment.bicep index bb16f5bd94..177a6c7c83 100644 --- a/avm/res/storage/storage-account/file-service/share/modules/nested_roleAssignment.bicep +++ b/avm/res/storage/storage-account/file-service/share/modules/nested_roleAssignment.bicep @@ -12,7 +12,7 @@ var builtInRoleNames = { 'Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349' ) - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/storage/storage-account/main.bicep b/avm/res/storage/storage-account/main.bicep index 9bcf736a9a..014c244abc 100644 --- a/avm/res/storage/storage-account/main.bicep +++ b/avm/res/storage/storage-account/main.bicep @@ -209,7 +209,7 @@ var builtInRoleNames = { 'Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349' ) - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index d73a89ee8f..38e3f3d998 100644 --- a/avm/res/storage/storage-account/main.json +++ b/avm/res/storage/storage-account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "13539810648097972278" + "templateHash": "6735651687082765200" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account.", @@ -965,7 +965,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "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')]", "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", @@ -2266,7 +2266,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "6152214765227449988" + "templateHash": "17077763197163073998" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service.", @@ -2664,7 +2664,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "2376441074312126168" + "templateHash": "1020003258393866601" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -2847,7 +2847,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "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')]", "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", @@ -3126,7 +3126,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11112322068652007329" + "templateHash": "1933197013743223154" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service.", @@ -3407,7 +3407,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "7774942141784896900" + "templateHash": "13477688809575027800" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share.", @@ -3614,7 +3614,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "17818484349715313082" + "templateHash": "10820882302387746924" } }, "parameters": { @@ -3737,7 +3737,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "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')]", "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", @@ -3890,7 +3890,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11962188257977966881" + "templateHash": "9552908737955027812" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service.", @@ -4135,7 +4135,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "18160215842105253661" + "templateHash": "1992900679572007532" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue.", @@ -4257,7 +4257,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "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')]", "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", @@ -4406,7 +4406,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11825608650296439803" + "templateHash": "15143318143658591417" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service.", @@ -4648,7 +4648,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16189309416113928106" + "templateHash": "16017327978473583176" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table.", @@ -4763,7 +4763,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "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')]", "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", diff --git a/avm/res/storage/storage-account/queue-service/main.json b/avm/res/storage/storage-account/queue-service/main.json index 535642de39..00065e2abe 100644 --- a/avm/res/storage/storage-account/queue-service/main.json +++ b/avm/res/storage/storage-account/queue-service/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11962188257977966881" + "templateHash": "9552908737955027812" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service.", @@ -251,7 +251,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "18160215842105253661" + "templateHash": "1992900679572007532" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue.", @@ -373,7 +373,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "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')]", "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", diff --git a/avm/res/storage/storage-account/queue-service/queue/main.bicep b/avm/res/storage/storage-account/queue-service/queue/main.bicep index 3dbc2a173d..4b0f656ef0 100644 --- a/avm/res/storage/storage-account/queue-service/queue/main.bicep +++ b/avm/res/storage/storage-account/queue-service/queue/main.bicep @@ -23,7 +23,7 @@ var builtInRoleNames = { 'Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349' ) - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/storage/storage-account/queue-service/queue/main.json b/avm/res/storage/storage-account/queue-service/queue/main.json index 9472e55002..8bea12f90e 100644 --- a/avm/res/storage/storage-account/queue-service/queue/main.json +++ b/avm/res/storage/storage-account/queue-service/queue/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "18160215842105253661" + "templateHash": "1992900679572007532" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue.", @@ -128,7 +128,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "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')]", "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", diff --git a/avm/res/storage/storage-account/table-service/main.json b/avm/res/storage/storage-account/table-service/main.json index c1ec65c05a..5582b11c4a 100644 --- a/avm/res/storage/storage-account/table-service/main.json +++ b/avm/res/storage/storage-account/table-service/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11825608650296439803" + "templateHash": "15143318143658591417" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service.", @@ -248,7 +248,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16189309416113928106" + "templateHash": "16017327978473583176" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table.", @@ -363,7 +363,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "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')]", "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", diff --git a/avm/res/storage/storage-account/table-service/table/main.bicep b/avm/res/storage/storage-account/table-service/table/main.bicep index 78ba2ed9e0..9b76e2f338 100644 --- a/avm/res/storage/storage-account/table-service/table/main.bicep +++ b/avm/res/storage/storage-account/table-service/table/main.bicep @@ -20,7 +20,7 @@ var builtInRoleNames = { 'Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349' ) - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/storage/storage-account/table-service/table/main.json b/avm/res/storage/storage-account/table-service/table/main.json index 5a86eccbdd..0476ee247e 100644 --- a/avm/res/storage/storage-account/table-service/table/main.json +++ b/avm/res/storage/storage-account/table-service/table/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16189309416113928106" + "templateHash": "16017327978473583176" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table.", @@ -121,7 +121,7 @@ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "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')]", "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", diff --git a/avm/res/storage/storage-account/version.json b/avm/res/storage/storage-account/version.json index 23f3815885..291fb73e82 100644 --- a/avm/res/storage/storage-account/version.json +++ b/avm/res/storage/storage-account/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.12", + "version": "0.13", "pathFilters": [ "./main.json" ] diff --git a/avm/res/synapse/private-link-hub/main.bicep b/avm/res/synapse/private-link-hub/main.bicep index 12ed80fc97..60406e8ea0 100644 --- a/avm/res/synapse/private-link-hub/main.bicep +++ b/avm/res/synapse/private-link-hub/main.bicep @@ -27,7 +27,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/synapse/private-link-hub/main.json b/avm/res/synapse/private-link-hub/main.json index b19850cd41..bdf5c2772a 100644 --- a/avm/res/synapse/private-link-hub/main.json +++ b/avm/res/synapse/private-link-hub/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "543021970961540498" + "templateHash": "6865603283108816559" }, "name": "Azure Synapse Analytics", "description": "This module deploys an Azure Synapse Analytics (Private Link Hub).", @@ -391,7 +391,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/synapse/private-link-hub/version.json b/avm/res/synapse/private-link-hub/version.json index a8eda31021..e42c3d9e5f 100644 --- a/avm/res/synapse/private-link-hub/version.json +++ b/avm/res/synapse/private-link-hub/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] diff --git a/avm/res/synapse/workspace/main.bicep b/avm/res/synapse/workspace/main.bicep index e396c1db28..e67c879082 100644 --- a/avm/res/synapse/workspace/main.bicep +++ b/avm/res/synapse/workspace/main.bicep @@ -129,7 +129,7 @@ var builtInRoleNames = { 'Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608' ) - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/synapse/workspace/main.json b/avm/res/synapse/workspace/main.json index 6b3b300ebf..a423ffb5a8 100644 --- a/avm/res/synapse/workspace/main.json +++ b/avm/res/synapse/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "2618570898047146464" + "templateHash": "3075067451671021037" }, "name": "Synapse Workspaces", "description": "This module deploys a Synapse Workspace.", @@ -750,7 +750,7 @@ "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')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/synapse/workspace/version.json b/avm/res/synapse/workspace/version.json index 7e1d3f4157..0f81d22abc 100644 --- a/avm/res/synapse/workspace/version.json +++ b/avm/res/synapse/workspace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.7", + "version": "0.8", "pathFilters": [ "./main.json" ] diff --git a/avm/res/virtual-machine-images/image-template/main.bicep b/avm/res/virtual-machine-images/image-template/main.bicep index c32e5e36f0..6d68abacc9 100644 --- a/avm/res/virtual-machine-images/image-template/main.bicep +++ b/avm/res/virtual-machine-images/image-template/main.bicep @@ -78,7 +78,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/virtual-machine-images/image-template/main.json b/avm/res/virtual-machine-images/image-template/main.json index df6d5ed0ea..8a2d773d7c 100644 --- a/avm/res/virtual-machine-images/image-template/main.json +++ b/avm/res/virtual-machine-images/image-template/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "495678619291242908" + "templateHash": "7173338220381253397" }, "name": "Virtual Machine Image Templates", "description": "This module deploys a Virtual Machine Image Template that can be consumed by Azure Image Builder (AIB).", @@ -559,7 +559,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/virtual-machine-images/image-template/version.json b/avm/res/virtual-machine-images/image-template/version.json index c177b1bb58..3f863a2bec 100644 --- a/avm/res/virtual-machine-images/image-template/version.json +++ b/avm/res/virtual-machine-images/image-template/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/web/connection/main.bicep b/avm/res/web/connection/main.bicep index caa5327bcf..db51edfc25 100644 --- a/avm/res/web/connection/main.bicep +++ b/avm/res/web/connection/main.bicep @@ -103,7 +103,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/web/connection/main.json b/avm/res/web/connection/main.json index e408288377..0d5894685a 100644 --- a/avm/res/web/connection/main.json +++ b/avm/res/web/connection/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11729288021799571934" + "templateHash": "1429787560788637007" }, "name": "API Connections", "description": "This module deploys an Azure API Connection.", @@ -230,7 +230,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, diff --git a/avm/res/web/connection/version.json b/avm/res/web/connection/version.json index c177b1bb58..3f863a2bec 100644 --- a/avm/res/web/connection/version.json +++ b/avm/res/web/connection/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] diff --git a/avm/res/web/site/main.bicep b/avm/res/web/site/main.bicep index 1df6f94be1..b7418015b6 100644 --- a/avm/res/web/site/main.bicep +++ b/avm/res/web/site/main.bicep @@ -197,7 +197,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/web/site/main.json b/avm/res/web/site/main.json index 5d92e94f37..e6eccb3688 100644 --- a/avm/res/web/site/main.json +++ b/avm/res/web/site/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4298333170970358862" + "templateHash": "7320044434284742277" }, "name": "Web/Function Apps", "description": "This module deploys a Web or Function App.", @@ -831,7 +831,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]" @@ -1677,7 +1677,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11723916533961427311" + "templateHash": "15729572124587777376" }, "name": "Web/Function App Deployment Slots", "description": "This module deploys a Web or Function App Deployment Slot.", @@ -2481,7 +2481,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]" diff --git a/avm/res/web/site/slot/main.bicep b/avm/res/web/site/slot/main.bicep index 1269d846dc..b871bf0960 100644 --- a/avm/res/web/site/slot/main.bicep +++ b/avm/res/web/site/slot/main.bicep @@ -183,7 +183,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/web/site/slot/main.json b/avm/res/web/site/slot/main.json index a81ad9d072..8f8f81e34a 100644 --- a/avm/res/web/site/slot/main.json +++ b/avm/res/web/site/slot/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "11723916533961427311" + "templateHash": "15729572124587777376" }, "name": "Web/Function App Deployment Slots", "description": "This module deploys a Web or Function App Deployment Slot.", @@ -810,7 +810,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]" diff --git a/avm/res/web/site/version.json b/avm/res/web/site/version.json index 7e1d3f4157..0f81d22abc 100644 --- a/avm/res/web/site/version.json +++ b/avm/res/web/site/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.7", + "version": "0.8", "pathFilters": [ "./main.json" ] diff --git a/avm/res/web/static-site/main.bicep b/avm/res/web/static-site/main.bicep index 8676687f1a..c4c51912de 100644 --- a/avm/res/web/static-site/main.bicep +++ b/avm/res/web/static-site/main.bicep @@ -104,7 +104,7 @@ var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') - 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Role Based Access Control Administrator': subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' ) diff --git a/avm/res/web/static-site/main.json b/avm/res/web/static-site/main.json index 81746c626a..490b5bd6df 100644 --- a/avm/res/web/static-site/main.json +++ b/avm/res/web/static-site/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "15796777918776565235" + "templateHash": "9780314991768462863" }, "name": "Static Web Apps", "description": "This module deploys a Static Web App.", @@ -536,7 +536,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", "Web Plan Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2cc479cb-7b4d-49a8-b449-8c00fd0f0a4b')]", "Website Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'de139f84-1756-47ae-9be6-808fbbe84772')]" diff --git a/avm/res/web/static-site/version.json b/avm/res/web/static-site/version.json index ea4f3b6e67..21226dd43f 100644 --- a/avm/res/web/static-site/version.json +++ b/avm/res/web/static-site/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] From e3b70587242c85aa14472f74aaec07775dffe5b9 Mon Sep 17 00:00:00 2001 From: "Menghua Chen (MSFT)" <111940661+Menghua1@users.noreply.github.com> Date: Mon, 9 Sep 2024 15:15:46 +0800 Subject: [PATCH 08/57] feat: Add new ptn modules `azd/insights-dashboard`. (#3175) ## Description Fixes https://github.com/Azure/Azure-Verified-Modules/issues/1206. ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.azd.insights-dashboard](https://github.com/Menghua1/bicep-registry-modules/actions/workflows/avm.ptn.azd.insights-dashboard.yml/badge.svg?branch=ptn%2Fazd%2Finsights-dashboard)](https://github.com/Menghua1/bicep-registry-modules/actions/workflows/avm.ptn.azd.insights-dashboard.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings @jongio for notification. --------- Co-authored-by: Alexander Sehr --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + .../avm.ptn.azd.insights-dashboard.yml | 88 + avm/ptn/azd/insights-dashboard/README.md | 272 ++ avm/ptn/azd/insights-dashboard/main.bicep | 111 + avm/ptn/azd/insights-dashboard/main.json | 2384 +++++++++++++++++ .../applicationinsights-dashboard.bicep | 1267 +++++++++ .../tests/e2e/defaults/dependencies.bicep | 30 + .../tests/e2e/defaults/main.test.bicep | 57 + .../tests/e2e/max/dependencies.bicep | 30 + .../tests/e2e/max/main.test.bicep | 60 + avm/ptn/azd/insights-dashboard/version.json | 7 + 12 files changed, 4308 insertions(+) create mode 100644 .github/workflows/avm.ptn.azd.insights-dashboard.yml create mode 100644 avm/ptn/azd/insights-dashboard/README.md create mode 100644 avm/ptn/azd/insights-dashboard/main.bicep create mode 100644 avm/ptn/azd/insights-dashboard/main.json create mode 100644 avm/ptn/azd/insights-dashboard/modules/applicationinsights-dashboard.bicep create mode 100644 avm/ptn/azd/insights-dashboard/tests/e2e/defaults/dependencies.bicep create mode 100644 avm/ptn/azd/insights-dashboard/tests/e2e/defaults/main.test.bicep create mode 100644 avm/ptn/azd/insights-dashboard/tests/e2e/max/dependencies.bicep create mode 100644 avm/ptn/azd/insights-dashboard/tests/e2e/max/main.test.bicep create mode 100644 avm/ptn/azd/insights-dashboard/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 6614ebf208..c294eb5bc4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -13,6 +13,7 @@ #/avm/ptn/avd-lza/networking/ @Azure/avm-ptn-avd-lza-networking-module-owners-bicep @Azure/avm-module-reviewers-bicep #/avm/ptn/avd-lza/session-hosts/ @Azure/avm-ptn-avd-lza-sessionhosts-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/azd/container-apps/ @Azure/avm-ptn-azd-containerapps-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/ptn/azd/insights-dashboard/ @Azure/avm-ptn-azd-insightsdashboard-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/deployment-script/import-image-to-acr/ @Azure/avm-ptn-deploymentscript-importimagetoacr-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/dev-ops/cicd-agents-and-runners/ @Azure/avm-ptn-devops-cicdagentsandrunners-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/finops-toolkit/finops-hub/ @Azure/avm-ptn-finopstoolkit-finopshub-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 51cfae077b..bc5bdfbed5 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -48,6 +48,7 @@ body: # - "avm/ptn/avd-lza/networking" # - "avm/ptn/avd-lza/session-hosts" - "avm/ptn/azd/container-apps" + - "avm/ptn/azd/insights-dashboard" - "avm/ptn/deployment-script/import-image-to-acr" - "avm/ptn/dev-ops/cicd-agents-and-runners" - "avm/ptn/finops-toolkit/finops-hub" diff --git a/.github/workflows/avm.ptn.azd.insights-dashboard.yml b/.github/workflows/avm.ptn.azd.insights-dashboard.yml new file mode 100644 index 0000000000..a1df3a89d7 --- /dev/null +++ b/.github/workflows/avm.ptn.azd.insights-dashboard.yml @@ -0,0 +1,88 @@ +name: "avm.ptn.azd.insights-dashboard" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.ptn.azd.insights-dashboard" + - "avm/ptn/azd/insights-dashboard/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/ptn/azd/insights-dashboard" + workflowPath: ".github/workflows/avm.ptn.azd.insights-dashboard.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit \ No newline at end of file diff --git a/avm/ptn/azd/insights-dashboard/README.md b/avm/ptn/azd/insights-dashboard/README.md new file mode 100644 index 0000000000..92e51c5d57 --- /dev/null +++ b/avm/ptn/azd/insights-dashboard/README.md @@ -0,0 +1,272 @@ +# Application Insights Components `[Azd/InsightsDashboard]` + +Creates an Application Insights instance based on an existing Log Analytics workspace. + +**Note:** This module is not intended for broad, generic use, as it was designed to cater for the requirements of the AZD CLI product. Feature requests and bug fix requests are welcome if they support the development of the AZD CLI but may not be incorporated if they aim to make this module more generic than what it needs to be for its primary use case. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/components` | [2020-02-02](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2020-02-02/components) | +| `microsoft.insights/components/linkedStorageAccounts` | [2020-03-01-preview](https://learn.microsoft.com/en-us/azure/templates/microsoft.insights/2020-03-01-preview/components/linkedStorageAccounts) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Portal/dashboards` | [2020-09-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Portal/2020-09-01-preview/dashboards) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/ptn/azd/insights-dashboard:`. + +- [Using only defaults](#example-1-using-only-defaults) +- [Using large parameter set](#example-2-using-large-parameter-set) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module insightsDashboard 'br/public:avm/ptn/azd/insights-dashboard:' = { + name: 'insightsDashboardDeployment' + params: { + // Required parameters + logAnalyticsWorkspaceResourceId: '' + name: 'aidmin001' + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

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

+ +### Example 2: _Using large parameter set_ + +This instance deploys the module using large parameters. + + +

+ +via Bicep module + +```bicep +module insightsDashboard 'br/public:avm/ptn/azd/insights-dashboard:' = { + name: 'insightsDashboardDeployment' + params: { + // Required parameters + logAnalyticsWorkspaceResourceId: '' + name: 'icmax001' + // Non-required parameters + applicationType: 'web' + dashboardName: 'icmaxdb001' + kind: 'web' + location: '' + } +} +``` + +
+

+ +

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

+ +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`logAnalyticsWorkspaceResourceId`](#parameter-loganalyticsworkspaceresourceid) | string | The resource ID of the loganalytics workspace. | +| [`name`](#parameter-name) | string | The resource insights components name. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`applicationType`](#parameter-applicationtype) | string | Application type. | +| [`dashboardName`](#parameter-dashboardname) | string | The resource portal dashboards name. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`kind`](#parameter-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`](#parameter-location) | string | Location for all Resources. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | + +### Parameter: `logAnalyticsWorkspaceResourceId` + +The resource ID of the loganalytics workspace. + +- Required: Yes +- Type: string + +### Parameter: `name` + +The resource insights components name. + +- Required: Yes +- Type: string + +### Parameter: `applicationType` + +Application type. + +- Required: No +- Type: string +- Default: `'web'` +- Allowed: + ```Bicep + [ + 'other' + 'web' + ] + ``` + +### Parameter: `dashboardName` + +The resource portal dashboards name. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `kind` + +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. + +- Required: No +- Type: string +- Default: `'web'` + +### Parameter: `location` + +Location for all Resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object +- Example: + ```Bicep + { + "key1": "value1" + "key2": "value2" + } + ``` + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `applicationInsightsConnectionString` | string | The connection string of the application insights. | +| `applicationInsightsInstrumentationKey` | string | The instrumentation key of the application insights. | +| `applicationInsightsName` | string | The name of the application insights. | +| `applicationInsightsResourceId` | string | The resource ID of the application insights. | +| `dashboardName` | string | The resource name of the dashboard. | +| `dashboardResourceId` | string | The resource ID of the dashboard. | +| `resourceGroupName` | string | The resource group the application insights components were deployed into. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `br/public:avm/res/insights/component:0.4.1` | Remote reference | +| `br/public:avm/res/portal/dashboard:0.1.0` | Remote reference | + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/ptn/azd/insights-dashboard/main.bicep b/avm/ptn/azd/insights-dashboard/main.bicep new file mode 100644 index 0000000000..911a2471a7 --- /dev/null +++ b/avm/ptn/azd/insights-dashboard/main.bicep @@ -0,0 +1,111 @@ +metadata name = 'Application Insights Components' +metadata description = '''Creates an Application Insights instance based on an existing Log Analytics workspace. + +**Note:** This module is not intended for broad, generic use, as it was designed to cater for the requirements of the AZD CLI product. Feature requests and bug fix requests are welcome if they support the development of the AZD CLI but may not be incorporated if they aim to make this module more generic than what it needs to be for its primary use case.''' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The resource insights components name.') +param name string + +@description('Optional. The resource portal dashboards name.') +param dashboardName string = '' + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Required. The resource ID of the loganalytics workspace.') +param logAnalyticsWorkspaceResourceId string + +@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 = 'web' + +@description('Optional. Application type.') +@allowed([ + 'web' + 'other' +]) +param applicationType string = 'web' + +@description('Optional. Tags of the resource.') +@metadata({ + example: ''' + { + "key1": "value1" + "key2": "value2" + } + ''' +}) +param tags object? + +// ============== // +// Resources // +// ============== // + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { + name: '46d3xbcp.ptn.azd-insightsdashboard.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } +} + +module applicationInsights 'br/public:avm/res/insights/component:0.4.1' = { + name: '${uniqueString(deployment().name, location)}-appinsights' + params: { + name: name + location: location + tags: tags + kind: kind + applicationType: applicationType + workspaceResourceId: logAnalyticsWorkspaceResourceId + } +} + +module applicationInsightsDashboard 'modules/applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.outputs.name + applicationInsightsResourceId: applicationInsights.outputs.resourceId + } +} + +// ============ // +// Outputs // +// ============ // + +@description('The resource group the application insights components were deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the application insights.') +output applicationInsightsName string = applicationInsights.outputs.name + +@description('The resource name of the dashboard.') +output dashboardName string = !empty(dashboardName) ? applicationInsightsDashboard.outputs.dashboardName : '' + +@description('The resource ID of the application insights.') +output applicationInsightsResourceId string = applicationInsights.outputs.resourceId + +@description('The resource ID of the dashboard.') +output dashboardResourceId string = !empty(dashboardName) ? applicationInsightsDashboard.outputs.dashboardResourceId : '' + +@description('The connection string of the application insights.') +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString + +@description('The instrumentation key of the application insights.') +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey diff --git a/avm/ptn/azd/insights-dashboard/main.json b/avm/ptn/azd/insights-dashboard/main.json new file mode 100644 index 0000000000..ed2893f7d0 --- /dev/null +++ b/avm/ptn/azd/insights-dashboard/main.json @@ -0,0 +1,2384 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "16185324363882257699" + }, + "name": "Application Insights Components", + "description": "Creates an Application Insights instance based on an existing Log Analytics workspace.\n\n**Note:** This module is not intended for broad, generic use, as it was designed to cater for the requirements of the AZD CLI product. Feature requests and bug fix requests are welcome if they support the development of the AZD CLI but may not be incorporated if they aim to make this module more generic than what it needs to be for its primary use case.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The resource insights components name." + } + }, + "dashboardName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource portal dashboards name." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the loganalytics workspace." + } + }, + "kind": { + "type": "string", + "defaultValue": "web", + "metadata": { + "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." + } + }, + "applicationType": { + "type": "string", + "defaultValue": "web", + "allowedValues": [ + "web", + "other" + ], + "metadata": { + "description": "Optional. Application type." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "example": " {\n \"key1\": \"value1\"\n \"key2\": \"value2\"\n }\n ", + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.ptn.azd-insightsdashboard.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "applicationInsights": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-appinsights', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "kind": { + "value": "[parameters('kind')]" + }, + "applicationType": { + "value": "[parameters('applicationType')]" + }, + "workspaceResourceId": { + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "10653241142071426932" + }, + "name": "Application Insights", + "description": "This component deploys an Application Insights instance.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Application Insights." + } + }, + "applicationType": { + "type": "string", + "defaultValue": "web", + "allowedValues": [ + "web", + "other" + ], + "metadata": { + "description": "Optional. Application type." + } + }, + "workspaceResourceId": { + "type": "string", + "metadata": { + "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." + } + }, + "disableIpMasking": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Disable IP masking. Default value is set to true." + } + }, + "disableLocalAuth": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Disable Non-AAD based Auth. Default value is set to false." + } + }, + "forceCustomerStorageForProfiler": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Force users to create their own storage account for profiler and debugger." + } + }, + "linkedStorageAccountResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Linked storage account resource ID." + } + }, + "publicNetworkAccessForIngestion": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The network access type for accessing Application Insights ingestion. - Enabled or Disabled." + } + }, + "publicNetworkAccessForQuery": { + "type": "string", + "defaultValue": "Enabled", + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The network access type for accessing Application Insights query. - Enabled or Disabled." + } + }, + "retentionInDays": { + "type": "int", + "defaultValue": 365, + "allowedValues": [ + 30, + 60, + 90, + 120, + 180, + 270, + 365, + 550, + 730 + ], + "metadata": { + "description": "Optional. Retention period in days." + } + }, + "samplingPercentage": { + "type": "int", + "defaultValue": 100, + "minValue": 0, + "maxValue": 100, + "metadata": { + "description": "Optional. Percentage of the data produced by the application being monitored that is being sampled for Application Insights telemetry." + } + }, + "kind": { + "type": "string", + "defaultValue": "", + "metadata": { + "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." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]", + "Monitoring Metrics Publisher": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb')]", + "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')]", + "Monitoring Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.insights-component.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "appInsights": { + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "kind": "[parameters('kind')]", + "properties": { + "Application_Type": "[parameters('applicationType')]", + "DisableIpMasking": "[parameters('disableIpMasking')]", + "DisableLocalAuth": "[parameters('disableLocalAuth')]", + "ForceCustomerStorageForProfiler": "[parameters('forceCustomerStorageForProfiler')]", + "WorkspaceResourceId": "[parameters('workspaceResourceId')]", + "publicNetworkAccessForIngestion": "[parameters('publicNetworkAccessForIngestion')]", + "publicNetworkAccessForQuery": "[parameters('publicNetworkAccessForQuery')]", + "RetentionInDays": "[parameters('retentionInDays')]", + "SamplingPercentage": "[parameters('samplingPercentage')]" + } + }, + "appInsights_roleAssignments": { + "copy": { + "name": "appInsights_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Insights/components/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Insights/components', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "appInsights" + ] + }, + "appInsights_diagnosticSettings": { + "copy": { + "name": "appInsights_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Insights/components/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "appInsights" + ] + }, + "linkedStorageAccount": { + "condition": "[not(empty(parameters('linkedStorageAccountResourceId')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-appInsights-linkedStorageAccount', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "appInsightsName": { + "value": "[parameters('name')]" + }, + "storageAccountResourceId": { + "value": "[parameters('linkedStorageAccountResourceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "216781367921725873" + }, + "name": "Application Insights Linked Storage Account", + "description": "This component deploys an Application Insights Linked Storage Account.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "appInsightsName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Application Insights instance. Required if the template is used in a standalone deployment." + } + }, + "storageAccountResourceId": { + "type": "string", + "metadata": { + "description": "Required. Linked storage account resource ID." + } + } + }, + "resources": [ + { + "type": "microsoft.insights/components/linkedStorageAccounts", + "apiVersion": "2020-03-01-preview", + "name": "[format('{0}/{1}', parameters('appInsightsName'), 'ServiceProfiler')]", + "properties": { + "linkedStorageAccount": "[parameters('storageAccountResourceId')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the Linked Storage Account." + }, + "value": "ServiceProfiler" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Linked Storage Account." + }, + "value": "[resourceId('microsoft.insights/components/linkedStorageAccounts', parameters('appInsightsName'), 'ServiceProfiler')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the agent pool was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "appInsights" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the application insights component." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the application insights component." + }, + "value": "[resourceId('Microsoft.Insights/components', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the application insights component was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "applicationId": { + "type": "string", + "metadata": { + "description": "The application ID of the application insights component." + }, + "value": "[reference('appInsights').AppId]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('appInsights', '2020-02-02', 'full').location]" + }, + "instrumentationKey": { + "type": "string", + "metadata": { + "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." + }, + "value": "[reference('appInsights').InstrumentationKey]" + }, + "connectionString": { + "type": "string", + "metadata": { + "description": "Application Insights Connection String." + }, + "value": "[reference('appInsights').ConnectionString]" + } + } + } + } + }, + "applicationInsightsDashboard": { + "condition": "[not(empty(parameters('dashboardName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "application-insights-dashboard", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('dashboardName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "applicationInsightsName": { + "value": "[reference('applicationInsights').outputs.name.value]" + }, + "applicationInsightsResourceId": { + "value": "[reference('applicationInsights').outputs.resourceId.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "12382758204242351890" + }, + "name": "Azure Portal Dashboard", + "description": "Creates a dashboard for an Application Insights instance.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The portal dashboard name." + } + }, + "applicationInsightsName": { + "type": "string", + "metadata": { + "description": "Required. The resource insights components name." + } + }, + "applicationInsightsResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource insights components ID." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "example": " {\n \"key1\": \"value1\"\n \"key2\": \"value2\"\n }\n ", + "description": "Optional. Tags of the resource." + } + } + }, + "resources": { + "dashboard": { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "dashboard-deployment", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('name')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "lenses": { + "value": [ + { + "order": 0, + "parts": [ + { + "position": { + "x": 0, + "y": 0, + "colSpan": 2, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "id", + "value": "[parameters('applicationInsightsResourceId')]" + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart", + "asset": { + "idInputName": "id", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "overview" + } + }, + { + "position": { + "x": 2, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "ProactiveDetection" + } + }, + { + "position": { + "x": 3, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "ResourceId", + "value": "[parameters('applicationInsightsResourceId')]" + } + ], + "type": "Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:20:33.345Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 5, + "y": 0, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-08T18:47:35.237Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "ConfigurationId", + "value": "78ce933e-e864-4b05-a27b-71fd55a6afad" + } + ], + "type": "Extension/AppInsightsExtension/PartType/AppMapButtonPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 0, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Usage", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 3, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "endTime": null, + "createdTime": "2018-05-04T01:22:35.782Z", + "isInitialTime": true, + "grain": 1, + "useDashboardTimeRange": false + } + } + ], + "type": "Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + } + } + }, + { + "position": { + "x": 4, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Reliability", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 7, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[parameters('applicationInsightsResourceId')]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:42:40.072Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "failures" + } + }, + { + "position": { + "x": 8, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Responsiveness\r\n", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 11, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ResourceId", + "value": "[parameters('applicationInsightsResourceId')]" + }, + { + "name": "DataModel", + "value": { + "version": "1.0.0", + "timeContext": { + "durationMs": 86400000, + "createdTime": "2018-05-04T23:43:37.804Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + "isOptional": true + }, + { + "name": "ConfigurationId", + "value": "2a8ede4f-2bee-4b9c-aed9-2db0e8a01865", + "isOptional": true + } + ], + "type": "Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart", + "isAdapter": true, + "asset": { + "idInputName": "ResourceId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "performance" + } + }, + { + "position": { + "x": 12, + "y": 1, + "colSpan": 3, + "rowSpan": 1 + }, + "metadata": { + "inputs": [], + "type": "Extension/HubsExtension/PartType/MarkdownPart", + "settings": { + "content": { + "settings": { + "content": "# Browser", + "title": "", + "subtitle": "" + } + } + } + } + }, + { + "position": { + "x": 15, + "y": 1, + "colSpan": 1, + "rowSpan": 1 + }, + "metadata": { + "inputs": [ + { + "name": "ComponentId", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "MetricsExplorerJsonDefinitionId", + "value": "BrowserPerformanceTimelineMetrics" + }, + { + "name": "TimeContext", + "value": { + "durationMs": 86400000, + "createdTime": "2018-05-08T12:16:27.534Z", + "isInitialTime": false, + "grain": 1, + "useDashboardTimeRange": false + } + }, + { + "name": "CurrentFilter", + "value": { + "eventTypes": [ + 4, + 1, + 3, + 5, + 2, + 6, + 13 + ], + "typeFacets": {}, + "isPermissive": false + } + }, + { + "name": "id", + "value": { + "Name": "[parameters('applicationInsightsName')]", + "SubscriptionId": "[subscription().subscriptionId]", + "ResourceGroup": "[resourceGroup().name]" + } + }, + { + "name": "Version", + "value": "1.0" + } + ], + "type": "Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart", + "asset": { + "idInputName": "ComponentId", + "type": "ApplicationInsights" + }, + "defaultMenuItemId": "browser" + } + }, + { + "position": { + "x": 0, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "sessions/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Sessions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "users/count", + "aggregationType": 5, + "namespace": "microsoft.insights/components/kusto", + "metricVisualization": { + "displayName": "Users", + "color": "#7E58FF" + } + } + ], + "title": "Unique sessions and users", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[parameters('applicationInsightsResourceId')]", + "menuid": "segmentationUsers" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "requests/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Failed requests", + "color": "#EC008C" + } + } + ], + "title": "Failed requests", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[parameters('applicationInsightsResourceId')]", + "menuid": "failures" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "requests/duration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server response time", + "color": "#00BCF2" + } + } + ], + "title": "Server response time", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[parameters('applicationInsightsResourceId')]", + "menuid": "performance" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 2, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "browserTimings/networkDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Page load network connect time", + "color": "#7E58FF" + } + }, + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "browserTimings/processingDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Client processing time", + "color": "#44F1C8" + } + }, + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "browserTimings/sendDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Send request time", + "color": "#EB9371" + } + }, + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "browserTimings/receiveDuration", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Receiving response time", + "color": "#0672F1" + } + } + ], + "title": "Average page load time breakdown", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "availabilityResults/availabilityPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability", + "color": "#47BDF5" + } + } + ], + "title": "Average availability", + "visualization": { + "chartType": 3, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + }, + "openBladeOnClick": { + "openBlade": true, + "destinationBlade": { + "extensionName": "HubsExtension", + "bladeName": "ResourceMenuBlade", + "parameters": { + "id": "[parameters('applicationInsightsResourceId')]", + "menuid": "availability" + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "exceptions/server", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Server exceptions", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "dependencies/failed", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Dependency failures", + "color": "#7E58FF" + } + } + ], + "title": "Server exceptions and Dependency failures", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "performanceCounters/processorCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Processor time", + "color": "#47BDF5" + } + }, + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "performanceCounters/processCpuPercentage", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process CPU", + "color": "#7E58FF" + } + } + ], + "title": "Average processor and process CPU utilization", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 12, + "y": 5, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "exceptions/browser", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Browser exceptions", + "color": "#47BDF5" + } + } + ], + "title": "Browser exceptions", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 0, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "availabilityResults/count", + "aggregationType": 7, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Availability test results count", + "color": "#47BDF5" + } + } + ], + "title": "Availability test results count", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 4, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "performanceCounters/processIOBytesPerSecond", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Process IO rate", + "color": "#47BDF5" + } + } + ], + "title": "Average process I/O rate", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + }, + { + "position": { + "x": 8, + "y": 8, + "colSpan": 4, + "rowSpan": 3 + }, + "metadata": { + "inputs": [ + { + "name": "options", + "value": { + "chart": { + "metrics": [ + { + "resourceMetadata": { + "id": "[parameters('applicationInsightsResourceId')]" + }, + "name": "performanceCounters/memoryAvailableBytes", + "aggregationType": 4, + "namespace": "microsoft.insights/components", + "metricVisualization": { + "displayName": "Available memory", + "color": "#47BDF5" + } + } + ], + "title": "Average available memory", + "visualization": { + "chartType": 2, + "legendVisualization": { + "isVisible": true, + "position": 2, + "hideSubtitle": false + }, + "axisVisualization": { + "x": { + "isVisible": true, + "axisType": 2 + }, + "y": { + "isVisible": true, + "axisType": 1 + } + } + } + } + } + }, + { + "name": "sharedTimeRange", + "isOptional": true + } + ], + "type": "Extension/HubsExtension/PartType/MonitorChartPart", + "settings": {} + } + } + ] + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "12676032921679464791" + }, + "name": "Portal Dashboards", + "description": "This module deploys a Portal Dashboard.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the dashboard to create." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "lenses": { + "type": "array", + "items": { + "type": "object" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The dashboard lenses." + } + }, + "metadata": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The dashboard metadata." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.portal-dashboard.{0}.{1}', replace('0.1.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "dashboard": { + "type": "Microsoft.Portal/dashboards", + "apiVersion": "2020-09-01-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "lenses": "[parameters('lenses')]", + "metadata": "[parameters('metadata')]" + } + }, + "dashboard_roleAssignments": { + "copy": { + "name": "dashboard_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Portal/dashboards/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Portal/dashboards', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "dashboard" + ] + }, + "dashboard_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Portal/dashboards/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "dashboard" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the dashboard." + }, + "value": "[resourceId('Microsoft.Portal/dashboards', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the dashboard was created in." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the dashboard." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the dashboard was deployed into." + }, + "value": "[reference('dashboard', '2020-09-01-preview', 'full').location]" + } + } + } + } + } + }, + "outputs": { + "dashboardResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the dashboard." + }, + "value": "[reference('dashboard').outputs.resourceId.value]" + }, + "dashboardName": { + "type": "string", + "metadata": { + "description": "The resource name of the dashboard." + }, + "value": "[reference('dashboard').outputs.name.value]" + } + } + } + }, + "dependsOn": [ + "applicationInsights" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the application insights components were deployed into." + }, + "value": "[resourceGroup().name]" + }, + "applicationInsightsName": { + "type": "string", + "metadata": { + "description": "The name of the application insights." + }, + "value": "[reference('applicationInsights').outputs.name.value]" + }, + "dashboardName": { + "type": "string", + "metadata": { + "description": "The resource name of the dashboard." + }, + "value": "[if(not(empty(parameters('dashboardName'))), reference('applicationInsightsDashboard').outputs.dashboardName.value, '')]" + }, + "applicationInsightsResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the application insights." + }, + "value": "[reference('applicationInsights').outputs.resourceId.value]" + }, + "dashboardResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the dashboard." + }, + "value": "[if(not(empty(parameters('dashboardName'))), reference('applicationInsightsDashboard').outputs.dashboardResourceId.value, '')]" + }, + "applicationInsightsConnectionString": { + "type": "string", + "metadata": { + "description": "The connection string of the application insights." + }, + "value": "[reference('applicationInsights').outputs.connectionString.value]" + }, + "applicationInsightsInstrumentationKey": { + "type": "string", + "metadata": { + "description": "The instrumentation key of the application insights." + }, + "value": "[reference('applicationInsights').outputs.instrumentationKey.value]" + } + } +} \ No newline at end of file diff --git a/avm/ptn/azd/insights-dashboard/modules/applicationinsights-dashboard.bicep b/avm/ptn/azd/insights-dashboard/modules/applicationinsights-dashboard.bicep new file mode 100644 index 0000000000..4a08622f49 --- /dev/null +++ b/avm/ptn/azd/insights-dashboard/modules/applicationinsights-dashboard.bicep @@ -0,0 +1,1267 @@ +metadata name = 'Azure Portal Dashboard' +metadata description = 'Creates a dashboard for an Application Insights instance.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The portal dashboard name.') +param name string + +@description('Required. The resource insights components name.') +param applicationInsightsName string + +@description('Required. The resource insights components ID.') +param applicationInsightsResourceId string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the resource.') +@metadata({ + example: ''' + { + "key1": "value1" + "key2": "value2" + } + ''' +}) +param tags object? + +// ============== // +// Resources // +// ============== // + +module dashboard 'br/public:avm/res/portal/dashboard:0.1.0' = { + name: 'dashboard-deployment' + params: { + name: name + location: location + tags: tags + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: applicationInsightsResourceId + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsightsName + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsightsName + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: applicationInsightsResourceId + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsightsName + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsightsName + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsightsName + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: applicationInsightsResourceId + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: applicationInsightsResourceId + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsightsName + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsightsName + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: applicationInsightsResourceId + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: applicationInsightsResourceId + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: applicationInsightsResourceId + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: applicationInsightsResourceId + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: applicationInsightsResourceId + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +// ============ // +// Outputs // +// ============ // + +@description('The resource ID of the dashboard.') +output dashboardResourceId string = dashboard.outputs.resourceId + +@description('The resource name of the dashboard.') +output dashboardName string = dashboard.outputs.name diff --git a/avm/ptn/azd/insights-dashboard/tests/e2e/defaults/dependencies.bicep b/avm/ptn/azd/insights-dashboard/tests/e2e/defaults/dependencies.bicep new file mode 100644 index 0000000000..3781246fcb --- /dev/null +++ b/avm/ptn/azd/insights-dashboard/tests/e2e/defaults/dependencies.bicep @@ -0,0 +1,30 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The resource operational insights workspaces name.') +param name string + +@description('Optional. Tags of the resource.') +@metadata({ + example: ''' + { + "key1": "value1" + "key2": "value2" + } + ''' +}) +param tags object? + +module logAnalytics 'br/public:avm/res/operational-insights/workspace:0.5.0' = { + name: '${uniqueString(deployment().name, location)}-loganalytics' + params: { + name: name + location: location + tags: tags + dataRetention: 30 + skuName: 'PerGB2018' + } +} + +@description('The resource ID of the loganalytics workspace.') +output logAnalyticsWorkspaceResourceId string = logAnalytics.outputs.resourceId diff --git a/avm/ptn/azd/insights-dashboard/tests/e2e/defaults/main.test.bicep b/avm/ptn/azd/insights-dashboard/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..b53b750d22 --- /dev/null +++ b/avm/ptn/azd/insights-dashboard/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,57 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-azd-insights-dashboard-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'aidmin' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module dependencies 'dependencies.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-dependencies' + scope: resourceGroup + params: { + name: 'dep-${namePrefix}-law-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + logAnalyticsWorkspaceResourceId: dependencies.outputs.logAnalyticsWorkspaceResourceId + } + } +] diff --git a/avm/ptn/azd/insights-dashboard/tests/e2e/max/dependencies.bicep b/avm/ptn/azd/insights-dashboard/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..3781246fcb --- /dev/null +++ b/avm/ptn/azd/insights-dashboard/tests/e2e/max/dependencies.bicep @@ -0,0 +1,30 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The resource operational insights workspaces name.') +param name string + +@description('Optional. Tags of the resource.') +@metadata({ + example: ''' + { + "key1": "value1" + "key2": "value2" + } + ''' +}) +param tags object? + +module logAnalytics 'br/public:avm/res/operational-insights/workspace:0.5.0' = { + name: '${uniqueString(deployment().name, location)}-loganalytics' + params: { + name: name + location: location + tags: tags + dataRetention: 30 + skuName: 'PerGB2018' + } +} + +@description('The resource ID of the loganalytics workspace.') +output logAnalyticsWorkspaceResourceId string = logAnalytics.outputs.resourceId diff --git a/avm/ptn/azd/insights-dashboard/tests/e2e/max/main.test.bicep b/avm/ptn/azd/insights-dashboard/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..fd3f0fe2ec --- /dev/null +++ b/avm/ptn/azd/insights-dashboard/tests/e2e/max/main.test.bicep @@ -0,0 +1,60 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set' +metadata description = 'This instance deploys the module using large parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-azd-insights-dashboard-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'icmax' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module dependencies 'dependencies.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-dependencies' + scope: resourceGroup + params: { + name: 'dep-${namePrefix}-law-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + logAnalyticsWorkspaceResourceId: dependencies.outputs.logAnalyticsWorkspaceResourceId + dashboardName: '${namePrefix}${serviceShort}db001' + kind: 'web' + applicationType: 'web' + } + } +] diff --git a/avm/ptn/azd/insights-dashboard/version.json b/avm/ptn/azd/insights-dashboard/version.json new file mode 100644 index 0000000000..7245f14872 --- /dev/null +++ b/avm/ptn/azd/insights-dashboard/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] + } \ No newline at end of file From 36b45011bcb5a433eeed4b26fae94325f6dc137c Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Mon, 9 Sep 2024 18:21:39 +0200 Subject: [PATCH 09/57] feat: Enable the introduction of `utl` modules in CI & utilities (#2756) ## Description - Enable the introduction of `utl` modules in CI (e.g., testing) & utilities (e.g., readme generation) - Using https://github.com/Azure/Azure-Verified-Modules/issues/898 as the backdrop - Updated readme to deal with functions and enabled it to only add specific parts if they have content (like telemetry disclaimer only if there's telemetry) Special changes - Utility modules don't necessarily have a telemetry deployment as they are not actually deployed - We may not have 'usage examples' in the readme for utility modules if they only export functions and don't actually deploy anything Notes/changes to static tests - Any module that does not deploy any parameters is not tested for telemetry (parameter/deployment/condition/output) - Resource group output is only expected for modules that contain resource deployments (as opposed to all modules in the rg scope) - utl (like ptn) modules are not tested for the existence of a `waf-aligned` e2e test - utl (like ptn) modules are not tested for the existence of a `defaults` e2e test - e2e tests only require a `serviceShort` parameter if they deploy resources (as opposed to always) - Noticed that the test was duplicated, so I removed one - e2e tests only require a `namePrefix` parameter if they deploy resources (as opposed to always) - e2e tests only require a module deployment using the syntax `module testDeployment '../.*main.bicep' = ` if they deploy resources (as opposed to always) - e2e tests only require `-test-` in their test deployment name only if they deploy resources (as opposed to always) Removal changes - As the example utility module only exports functions, the removal logic had to be adapted to not only work if resources are deployed - The logic now differentiates in between 'I can not find a deployment' and 'I can find a deployment, but it has no resources linked' (before it was only the former) - Further, the logic was slightly altered to not give up as soon as no deployment is found, but instead try and find as many as possible and only throw an exception after the removal logic ran end to end. If interested, [this](https://github.com/AlexanderSehr/bicep-registry-modules/pull/6/files) is how modules would look like. TODO - [x] Find out why bicep build fails the example template - [x] Test CI with new utility module(s) - [x] Retest CI for res & ptn modules - [x] Run all custom utilities for res & ptn, as well as utl modules - [x] Remove `avm/utl/general/get-environment` function after all tests passed - [x] Remove `avm/utl/general/any-deployment-script` function after all tests passed Closes #2755 ## Pipeline Reference | Pipeline | | -------- | | [![avm.utl.general.any-deployment-script](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.utl.general.any-deployment-script.yml/badge.svg)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.utl.general.any-deployment-script.yml) (fails because the module isn't existing in AVM) | | [Successful deployment run](https://github.com/AlexanderSehr/bicep-registry-modules/actions/runs/10130797213/job/28012687558) | | [![avm.utl.general.get-environment](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.utl.general.get-environment.yml/badge.svg)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.utl.general.get-environment.yml) | | [Successful deployment run](https://github.com/AlexanderSehr/bicep-registry-modules/actions/runs/10130869534) | [![avm.res.container-registry.registry](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-registry.registry.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-registry.registry.yml)

All workflows run | Pipeline | | -------- | [![avm.ptn.ai-platform.baseline](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.ai-platform.baseline.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.ai-platform.baseline.yml) [![avm.ptn.authorization.policy-assignment](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.authorization.policy-assignment.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.authorization.policy-assignment.yml) [![avm.ptn.authorization.resource-role-assignment](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.authorization.resource-role-assignment.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.authorization.resource-role-assignment.yml) [![avm.ptn.authorization.role-assignment](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.authorization.role-assignment.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.authorization.role-assignment.yml) [![avm.ptn.deployment-script.import-image-to-acr](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.deployment-script.import-image-to-acr.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.deployment-script.import-image-to-acr.yml) [![avm.ptn.finops-toolkit.finops-hub](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.finops-toolkit.finops-hub.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.finops-toolkit.finops-hub.yml) [![avm.ptn.lz.sub-vending](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.lz.sub-vending.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.lz.sub-vending.yml) [![avm.ptn.network.private-link-private-dns-zones](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.network.private-link-private-dns-zones.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.network.private-link-private-dns-zones.yml) [![avm.ptn.policy-insights.remediation](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.policy-insights.remediation.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.policy-insights.remediation.yml) [![avm.ptn.security.security-center](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.security.security-center.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.security.security-center.yml) [![avm.res.aad.domain-service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.aad.domain-service.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.aad.domain-service.yml) [![avm.res.alerts-management.action-rule](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.alerts-management.action-rule.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.alerts-management.action-rule.yml) [![avm.res.analysis-services.server](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.analysis-services.server.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.analysis-services.server.yml) [![avm.res.api-management.service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.api-management.service.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.api-management.service.yml) [![avm.res.app-configuration.configuration-store](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app-configuration.configuration-store.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app-configuration.configuration-store.yml) [![avm.res.app.container-app](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.container-app.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.container-app.yml) [![avm.res.app.job](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.job.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.job.yml) [![avm.res.app.managed-environment](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.managed-environment.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.managed-environment.yml) [![avm.res.automation.automation-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.automation.automation-account.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.automation.automation-account.yml) [![avm.res.batch.batch-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.batch.batch-account.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.batch.batch-account.yml) [![avm.res.cache.redis](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cache.redis.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cache.redis.yml) [![avm.res.cdn.profile](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cdn.profile.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cdn.profile.yml) [![avm.res.cognitive-services.account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cognitive-services.account.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cognitive-services.account.yml) [![avm.res.communication.communication-service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.communication.communication-service.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.communication.communication-service.yml) [![avm.res.communication.email-service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.communication.email-service.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.communication.email-service.yml) [![avm.res.compute.availability-set](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.availability-set.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.availability-set.yml) [![avm.res.compute.disk-encryption-set](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.disk-encryption-set.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.disk-encryption-set.yml) [![avm.res.compute.disk](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.disk.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.disk.yml) [![avm.res.compute.gallery](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.gallery.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.gallery.yml) [![avm.res.compute.image](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.image.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.image.yml) [![avm.res.compute.proximity-placement-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.proximity-placement-group.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.proximity-placement-group.yml) [![avm.res.compute.ssh-public-key](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.ssh-public-key.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.ssh-public-key.yml) [![avm.res.compute.virtual-machine-scale-set](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine-scale-set.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine-scale-set.yml) [![avm.res.compute.virtual-machine](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine.yml) [![avm.res.consumption.budget](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.consumption.budget.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.consumption.budget.yml) [![avm.res.container-instance.container-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-instance.container-group.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-instance.container-group.yml) [![avm.res.container-registry.registry](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-registry.registry.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-registry.registry.yml) [![avm.res.container-service.managed-cluster](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml) [![avm.res.data-factory.factory](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.data-factory.factory.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.data-factory.factory.yml) [![avm.res.data-protection.backup-vault](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.data-protection.backup-vault.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.data-protection.backup-vault.yml) [![avm.res.databricks.access-connector](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.databricks.access-connector.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.databricks.access-connector.yml) [![avm.res.databricks.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml) [![avm.res.db-for-my-sql.flexible-server](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.db-for-my-sql.flexible-server.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.db-for-my-sql.flexible-server.yml) (unrelated, WAF-reliability tests failed) [![avm.res.db-for-postgre-sql.flexible-server](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.db-for-postgre-sql.flexible-server.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.db-for-postgre-sql.flexible-server.yml) (unrelated, WAF-reliability tests failed) [![avm.res.desktop-virtualization.application-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.application-group.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.application-group.yml) [![avm.res.desktop-virtualization.host-pool](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.host-pool.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.host-pool.yml) (unrelated, WAF-reliability tests failed) [![avm.res.desktop-virtualization.scaling-plan](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.scaling-plan.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.scaling-plan.yml) [![avm.res.desktop-virtualization.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.workspace.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.workspace.yml) [![avm.res.dev-test-lab.lab](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.dev-test-lab.lab.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.dev-test-lab.lab.yml) [![avm.res.digital-twins.digital-twins-instance](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.digital-twins.digital-twins-instance.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.digital-twins.digital-twins-instance.yml) [![avm.res.document-db.database-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.document-db.database-account.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.document-db.database-account.yml) (unrelated, WAF-reliability tests failed) [![avm.res.event-grid.domain](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.domain.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.domain.yml) [![avm.res.event-grid.namespace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.namespace.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.namespace.yml) [![avm.res.event-grid.system-topic](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.system-topic.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.system-topic.yml) [![avm.res.event-grid.topic](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.topic.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.topic.yml) [![avm.res.event-hub.namespace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-hub.namespace.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-hub.namespace.yml) [![avm.res.health-bot.health-bot](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.health-bot.health-bot.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.health-bot.health-bot.yml) [![avm.res.healthcare-apis.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.healthcare-apis.workspace.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.healthcare-apis.workspace.yml) [![avm.res.hybrid-compute.machine](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.hybrid-compute.machine.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.hybrid-compute.machine.yml) [![avm.res.insights.action-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.action-group.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.action-group.yml) [![avm.res.insights.activity-log-alert](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.activity-log-alert.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.activity-log-alert.yml) [![avm.res.insights.component](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.component.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.component.yml) [![avm.res.insights.data-collection-endpoint](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.data-collection-endpoint.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.data-collection-endpoint.yml) [![avm.res.insights.data-collection-rule](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.data-collection-rule.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.data-collection-rule.yml) [![avm.res.insights.diagnostic-setting](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.diagnostic-setting.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.diagnostic-setting.yml) [![avm.res.insights.metric-alert](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.metric-alert.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.metric-alert.yml) [![avm.res.insights.private-link-scope](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.private-link-scope.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.private-link-scope.yml) [![avm.res.insights.scheduled-query-rule](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.scheduled-query-rule.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.scheduled-query-rule.yml) [![avm.res.insights.webtest](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.webtest.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.webtest.yml) [![avm.res.key-vault.vault](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml) [![avm.res.kubernetes-configuration.extension](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.kubernetes-configuration.extension.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.kubernetes-configuration.extension.yml) [![avm.res.kubernetes-configuration.flux-configuration](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.kubernetes-configuration.flux-configuration.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.kubernetes-configuration.flux-configuration.yml) [![avm.res.kusto.cluster](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.kusto.cluster.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.kusto.cluster.yml) [![avm.res.load-test-service.load-test](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.load-test-service.load-test.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.load-test-service.load-test.yml) [![avm.res.logic.workflow](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.logic.workflow.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.logic.workflow.yml) [![avm.res.machine-learning-services.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.machine-learning-services.workspace.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.machine-learning-services.workspace.yml) [![avm.res.maintenance.maintenance-configuration](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.maintenance.maintenance-configuration.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.maintenance.maintenance-configuration.yml) [![avm.res.managed-identity.user-assigned-identity](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.managed-identity.user-assigned-identity.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.managed-identity.user-assigned-identity.yml) [![avm.res.managed-services.registration-definition](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.managed-services.registration-definition.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.managed-services.registration-definition.yml) [![avm.res.management.management-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.management.management-group.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.management.management-group.yml) [![avm.res.net-app.net-app-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.net-app.net-app-account.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.net-app.net-app-account.yml) [![avm.res.network.application-gateway-web-application-firewall-policy](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway-web-application-firewall-policy.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway-web-application-firewall-policy.yml) (unrelated, WAF-reliability tests failed) [![avm.res.network.application-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway.yml) [![avm.res.network.application-security-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-security-group.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-security-group.yml) [![avm.res.network.azure-firewall](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.azure-firewall.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.azure-firewall.yml) [![avm.res.network.bastion-host](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.bastion-host.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.bastion-host.yml) [![avm.res.network.connection](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.connection.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.connection.yml) [![avm.res.network.ddos-protection-plan](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.ddos-protection-plan.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.ddos-protection-plan.yml) [![avm.res.network.dns-forwarding-ruleset](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-forwarding-ruleset.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-forwarding-ruleset.yml) [![avm.res.network.dns-resolver](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-resolver.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-resolver.yml) [![avm.res.network.dns-zone](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-zone.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-zone.yml) [![avm.res.network.express-route-circuit](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.express-route-circuit.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.express-route-circuit.yml) [![avm.res.network.express-route-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.express-route-gateway.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.express-route-gateway.yml) [![avm.res.network.firewall-policy](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.firewall-policy.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.firewall-policy.yml) [![avm.res.network.front-door-web-application-firewall-policy](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.front-door-web-application-firewall-policy.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.front-door-web-application-firewall-policy.yml) [![avm.res.network.front-door](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.front-door.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.front-door.yml) [![avm.res.network.ip-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.ip-group.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.ip-group.yml) [![avm.res.network.load-balancer](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.load-balancer.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.load-balancer.yml) [![avm.res.network.local-network-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.local-network-gateway.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.local-network-gateway.yml) [![avm.res.network.nat-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.nat-gateway.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.nat-gateway.yml) [![avm.res.network.network-interface](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-interface.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-interface.yml) [![avm.res.network.network-manager](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-manager.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-manager.yml) [![avm.res.network.network-security-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-security-group.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-security-group.yml) [![avm.res.network.network-watcher](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-watcher.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-watcher.yml) [![avm.res.network.private-dns-zone](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-dns-zone.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-dns-zone.yml) [![avm.res.network.private-endpoint](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-endpoint.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-endpoint.yml) [![avm.res.network.private-link-service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-link-service.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-link-service.yml) [![avm.res.network.public-ip-address](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-address.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-address.yml) [![avm.res.network.public-ip-prefix](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-prefix.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-prefix.yml) [![avm.res.network.route-table](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.route-table.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.route-table.yml) [![avm.res.network.service-endpoint-policy](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.service-endpoint-policy.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.service-endpoint-policy.yml) [![avm.res.network.trafficmanagerprofile](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.trafficmanagerprofile.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.trafficmanagerprofile.yml) [![avm.res.network.virtual-hub](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-hub.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-hub.yml) [![avm.res.network.virtual-network-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network-gateway.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network-gateway.yml) [![avm.res.network.virtual-network](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network.yml) [![avm.res.network.virtual-wan](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-wan.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-wan.yml) [![avm.res.network.vpn-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.vpn-gateway.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.vpn-gateway.yml) [![avm.res.network.vpn-site](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.vpn-site.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.vpn-site.yml) [![avm.res.operational-insights.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.operational-insights.workspace.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.operational-insights.workspace.yml) [![avm.res.operations-management.solution](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.operations-management.solution.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.operations-management.solution.yml) [![avm.res.portal.dashboard](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.portal.dashboard.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.portal.dashboard.yml) [![avm.res.power-bi-dedicated.capacity](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.power-bi-dedicated.capacity.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.power-bi-dedicated.capacity.yml) [![avm.res.purview.account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.purview.account.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.purview.account.yml) [![avm.res.recovery-services.vault](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.recovery-services.vault.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.recovery-services.vault.yml) [![avm.res.relay.namespace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.relay.namespace.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.relay.namespace.yml) [![avm.res.resource-graph.query](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resource-graph.query.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resource-graph.query.yml) [![avm.res.resources.deployment-script](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resources.deployment-script.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resources.deployment-script.yml) [![avm.res.resources.resource-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resources.resource-group.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resources.resource-group.yml) [![avm.res.search.search-service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.search.search-service.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.search.search-service.yml) [![avm.res.service-bus.namespace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.service-bus.namespace.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.service-bus.namespace.yml) [![avm.res.service-fabric.cluster](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.service-fabric.cluster.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.service-fabric.cluster.yml) [![avm.res.signal-r-service.signal-r](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.signal-r.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.signal-r.yml) [![avm.res.signal-r-service.web-pub-sub](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.web-pub-sub.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.web-pub-sub.yml) [![avm.res.sql.instance-pool](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.instance-pool.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.instance-pool.yml) [![avm.res.sql.managed-instance](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.managed-instance.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.managed-instance.yml) (unrelated, WAF-reliability tests failed) [![avm.res.sql.server](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.server.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.server.yml) [![avm.res.storage.storage-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml) [![avm.res.synapse.private-link-hub](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.synapse.private-link-hub.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.synapse.private-link-hub.yml) [![avm.res.synapse.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.synapse.workspace.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.synapse.workspace.yml) [![avm.res.virtual-machine-images.image-template](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.virtual-machine-images.image-template.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.virtual-machine-images.image-template.yml) [![avm.res.web.connection](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.connection.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.connection.yml) [![avm.res.web.hosting-environment](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.hosting-environment.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.hosting-environment.yml) (unrealted, WAF-reliablity tests failed) [![avm.res.web.serverfarm](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.serverfarm.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.serverfarm.yml) (unrealted, WAF-reliablity tests failed) [![avm.res.web.site](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.site.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.site.yml) [![avm.res.web.static-site](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.static-site.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.static-site.yml) [![avm.utl.general.any-deployment-script](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.utl.general.any-deployment-script.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.utl.general.any-deployment-script.yml) [![avm.utl.general.get-environment](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.utl.general.get-environment.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.utl.general.get-environment.yml)
Platform pipelines | Pipeline | | - | [![.Platform - Check PSRule](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/platform.check.psrule.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/platform.check.psrule.yml) (unrelated) [![.Platform - Clean up deployment history](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/platform.deployment.history.cleanup.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/platform.deployment.history.cleanup.yml) [![.Platform - Manage workflow issue](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/platform.manage-workflow-issue.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/platform.manage-workflow-issue.yml) (unrelated permission issue) [![.Platform - Publish [moduleIndex.json]](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/platform.publish-module-index-json.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/platform.publish-module-index-json.yml) (unrelated permission issue) [![.Platform - Run CI tests](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/platform.ci-tests.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/platform.ci-tests.yml) [![.Platform - Sync repo labels from CSV](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/platform.sync-repo-labels-from-csv.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/platform.sync-repo-labels-from-csv.yml) [![.Platform - Toggle AVM workflows](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/platform.toggle-avm-workflows.yml/badge.svg?branch=users%2Falsehr%2Futl&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/platform.toggle-avm-workflows.yml)
## Type of Change - [x] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --------- Co-authored-by: Maher Aldineh Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- .github/workflows/avm.template.module.yml | 2 +- .../platform.toggle-avm-workflows.yml | 2 +- avm/ptn/azd/container-apps/README.md | 2 - .../registry/cache-rule/README.md | 4 +- .../registry/cache-rule/main.bicep | 2 +- .../registry/cache-rule/main.json | 4 +- avm/res/container-registry/registry/main.json | 4 +- .../Get-AvailableResourceLocation.ps1 | 2 +- .../Get-DeploymentTargetResourceList.ps1 | 2 +- .../Invoke-AvmJsonModuleIndexGeneration.ps1 | 2 +- .../Set-AvmGitHubIssueOwnerConfig.ps1 | 2 +- .../Set-AvmGithubIssueForWorkflow.ps1 | 2 +- .../platform/Switch-WorkflowState.ps1 | 6 +- .../platform/Sync-AvmModulesList.ps1 | 58 +++++- .../publish/helper/Get-ModuleReadmeLink.ps1 | 2 +- .../helper/Get-ModuleTargetPatchVersion.ps1 | 2 +- .../publish/helper/Get-ModulesToPublish.ps1 | 4 +- .../publish/helper/New-ModuleReleaseTag.ps1 | 2 +- .../sharedScripts/Get-BRMRepositoryName.ps1 | 2 +- .../helper/Get-CrossReferencedModuleList.ps1 | 4 +- .../compliance/Set-PesterGitHubOutput.ps1 | 2 +- .../compliance/helper/helper.psm1 | 5 +- .../compliance/module.tests.ps1 | 169 ++++++++++++------ 23 files changed, 198 insertions(+), 88 deletions(-) diff --git a/.github/workflows/avm.template.module.yml b/.github/workflows/avm.template.module.yml index 55bbbc0abd..4df961c68e 100644 --- a/.github/workflows/avm.template.module.yml +++ b/.github/workflows/avm.template.module.yml @@ -140,7 +140,7 @@ jobs: job_publish_module: # Note: Please don't change this job name. It is used by the setEnvironment action to define which PS modules to install on runners. name: "Publishing" runs-on: ubuntu-latest - # Note: Below always() required in condition due to psrule jobs being skipped for ptn modules not having defaults or waf-aligned folders + # Note: Below always() required in condition due to psrule jobs being skipped for ptn & utl modules not having defaults or waf-aligned folders if: github.ref == 'refs/heads/main' && github.repository == 'Azure/bicep-registry-modules' && always() && diff --git a/.github/workflows/platform.toggle-avm-workflows.yml b/.github/workflows/platform.toggle-avm-workflows.yml index 468f942a5b..2c119b2473 100644 --- a/.github/workflows/platform.toggle-avm-workflows.yml +++ b/.github/workflows/platform.toggle-avm-workflows.yml @@ -15,7 +15,7 @@ on: type: string description: "RegEx which workflows are included" required: false - default: "avm\\.(?:res|ptn)" + default: "avm\\.(?:res|ptn|utl)" excludePattern: type: string description: "RegEx which workflows are excluded" diff --git a/avm/ptn/azd/container-apps/README.md b/avm/ptn/azd/container-apps/README.md index 144948db21..413bf894be 100644 --- a/avm/ptn/azd/container-apps/README.md +++ b/avm/ptn/azd/container-apps/README.md @@ -145,7 +145,6 @@ module containerApps 'br/public:avm/ptn/azd/container-apps:' = {

- ## Parameters **Required parameters** @@ -338,7 +337,6 @@ Zone redundancy setting. - Type: bool - Default: `True` - ## Outputs | Output | Type | Description | diff --git a/avm/res/container-registry/registry/cache-rule/README.md b/avm/res/container-registry/registry/cache-rule/README.md index c5b94b0a52..064d6dc202 100644 --- a/avm/res/container-registry/registry/cache-rule/README.md +++ b/avm/res/container-registry/registry/cache-rule/README.md @@ -28,7 +28,7 @@ Cache for Azure Container Registry (Preview) feature allows users to cache conta | Parameter | Type | Description | | :-- | :-- | :-- | -| [`name`](#parameter-name) | string | The name of the cache rule. Will be dereived from the source repository name if not defined. | +| [`name`](#parameter-name) | string | The name of the cache rule. Will be derived from the source repository name if not defined. | | [`targetRepository`](#parameter-targetrepository) | string | Target repository specified in docker pull command. E.g.: docker pull myregistry.azurecr.io/{targetRepository}:{tag}. | ### Parameter: `credentialSetResourceId` @@ -54,7 +54,7 @@ Source repository pulled from upstream. ### Parameter: `name` -The name of the cache rule. Will be dereived from the source repository name if not defined. +The name of the cache rule. Will be derived from the source repository name if not defined. - Required: No - Type: string diff --git a/avm/res/container-registry/registry/cache-rule/main.bicep b/avm/res/container-registry/registry/cache-rule/main.bicep index 55a1c1387c..3a58558bc3 100644 --- a/avm/res/container-registry/registry/cache-rule/main.bicep +++ b/avm/res/container-registry/registry/cache-rule/main.bicep @@ -5,7 +5,7 @@ metadata owner = 'Azure/module-maintainers' @description('Required. The name of the parent registry. Required if the template is used in a standalone deployment.') param registryName string -@description('Optional. The name of the cache rule. Will be dereived from the source repository name if not defined.') +@description('Optional. The name of the cache rule. Will be derived from the source repository name if not defined.') param name string = replace(replace(sourceRepository, '/', '-'), '.', '-') @description('Required. Source repository pulled from upstream.') diff --git a/avm/res/container-registry/registry/cache-rule/main.json b/avm/res/container-registry/registry/cache-rule/main.json index c517e75779..3536c9537a 100644 --- a/avm/res/container-registry/registry/cache-rule/main.json +++ b/avm/res/container-registry/registry/cache-rule/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4294329625336671928" + "templateHash": "14369419482171687857" }, "name": "Container Registries Cache", "description": "Cache for Azure Container Registry (Preview) feature allows users to cache container images in a private container registry. Cache for ACR, is a preview feature available in Basic, Standard, and Premium service tiers ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache)).", @@ -22,7 +22,7 @@ "type": "string", "defaultValue": "[replace(replace(parameters('sourceRepository'), '/', '-'), '.', '-')]", "metadata": { - "description": "Optional. The name of the cache rule. Will be dereived from the source repository name if not defined." + "description": "Optional. The name of the cache rule. Will be derived from the source repository name if not defined." } }, "sourceRepository": { diff --git a/avm/res/container-registry/registry/main.json b/avm/res/container-registry/registry/main.json index 5b71f153bf..bc420f1e32 100644 --- a/avm/res/container-registry/registry/main.json +++ b/avm/res/container-registry/registry/main.json @@ -1479,7 +1479,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4294329625336671928" + "templateHash": "14369419482171687857" }, "name": "Container Registries Cache", "description": "Cache for Azure Container Registry (Preview) feature allows users to cache container images in a private container registry. Cache for ACR, is a preview feature available in Basic, Standard, and Premium service tiers ([ref](https://learn.microsoft.com/en-us/azure/container-registry/tutorial-registry-cache)).", @@ -1496,7 +1496,7 @@ "type": "string", "defaultValue": "[replace(replace(parameters('sourceRepository'), '/', '-'), '.', '-')]", "metadata": { - "description": "Optional. The name of the cache rule. Will be dereived from the source repository name if not defined." + "description": "Optional. The name of the cache rule. Will be derived from the source repository name if not defined." } }, "sourceRepository": { diff --git a/avm/utilities/pipelines/e2eValidation/regionSelector/Get-AvailableResourceLocation.ps1 b/avm/utilities/pipelines/e2eValidation/regionSelector/Get-AvailableResourceLocation.ps1 index 3ea25a2600..69cac89b0c 100644 --- a/avm/utilities/pipelines/e2eValidation/regionSelector/Get-AvailableResourceLocation.ps1 +++ b/avm/utilities/pipelines/e2eValidation/regionSelector/Get-AvailableResourceLocation.ps1 @@ -66,7 +66,7 @@ function Get-AvailableResourceLocation { . (Join-Path $RepoRoot 'avm' 'utilities' 'pipelines' 'sharedScripts' 'helper' 'Get-SpecsAlignedResourceName.ps1') # Configure Resource Type - $fullModuleIdentifier = ($ModuleRoot -split '[\/|\\]{0,1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' + $fullModuleIdentifier = ($ModuleRoot -split '[\/|\\]{0,1}avm[\/|\\]{1}(res|ptn|utl)[\/|\\]{1}')[2] -replace '\\', '/' if ($ModuleRoot -like 'avm/res*') { diff --git a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Get-DeploymentTargetResourceList.ps1 b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Get-DeploymentTargetResourceList.ps1 index 24d76e58d9..3338aef342 100644 --- a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Get-DeploymentTargetResourceList.ps1 +++ b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Get-DeploymentTargetResourceList.ps1 @@ -1,4 +1,4 @@ -#region helper +#region helper <# .SYNOPSIS Get all deployment operations at a given scope diff --git a/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 b/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 index 03dd99c99c..0c0a5a2f07 100644 --- a/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 +++ b/avm/utilities/pipelines/platform/Invoke-AvmJsonModuleIndexGeneration.ps1 @@ -74,7 +74,7 @@ function Invoke-AvmJsonModuleIndexGeneration { $anyErrorsOccurred = $false $moduleIndexData = @() - foreach ($avmModuleRoot in @('avm/res', 'avm/ptn')) { + foreach ($avmModuleRoot in @('avm/res', 'avm/ptn', 'avm/utl')) { $avmModuleGroups = (Get-ChildItem -Path $avmModuleRoot -Directory).Name foreach ($moduleGroup in $avmModuleGroups) { diff --git a/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 b/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 index afc1d33bd3..d38bd012b9 100644 --- a/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 +++ b/avm/utilities/pipelines/platform/Set-AvmGitHubIssueOwnerConfig.ps1 @@ -40,7 +40,7 @@ function Set-AvmGitHubIssueOwnerConfig { $issue = gh issue view $IssueUrl.Replace('api.', '').Replace('repos/', '') --json 'author,title,url,body,comments' --repo $Repo | ConvertFrom-Json -Depth 100 if ($issue.title.StartsWith('[AVM Module Issue]')) { - $moduleName = ($issue.body.Split("`n") -match 'avm/(?:res|ptn)')[0].Trim().Replace(' ', '') + $moduleName = ($issue.body.Split("`n") -match 'avm/(?:res|ptn|utl)')[0].Trim().Replace(' ', '') if ([string]::IsNullOrEmpty($moduleName)) { throw 'No valid module name was found in the issue.' diff --git a/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 b/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 index 07c0a28295..4d91fa68d5 100644 --- a/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 +++ b/avm/utilities/pipelines/platform/Set-AvmGithubIssueForWorkflow.ps1 @@ -100,7 +100,7 @@ function Set-AvmGithubIssueForWorkflow { > @Azure/avm-core-team-technical-bicep, the workflow for the ``$moduleName`` module has failed. Please investigate the failed workflow run. "@ - if ($workflowRun.workflowName -match 'avm.(?:res|ptn)') { + if ($workflowRun.workflowName -match 'avm.(?:res|ptn|utl)') { $moduleIndex = $moduleName.StartsWith('avm/res') ? 'Bicep-Resource' : 'Bicep-Pattern' # get CSV data $module = Get-AvmCsvData -ModuleIndex $moduleIndex | Where-Object ModuleName -EQ $moduleName diff --git a/avm/utilities/pipelines/platform/Switch-WorkflowState.ps1 b/avm/utilities/pipelines/platform/Switch-WorkflowState.ps1 index 1d02a854e4..abd7b9f878 100644 --- a/avm/utilities/pipelines/platform/Switch-WorkflowState.ps1 +++ b/avm/utilities/pipelines/platform/Switch-WorkflowState.ps1 @@ -15,7 +15,7 @@ Mandatory. The owning organization of the repository. For example, 'MyOrg'. Optional. The name of the repository. Defaults to 'bicep-registry-modules'. .PARAMETER IncludePattern -Optional. A regex pattern to match against the workflow names. Defaults to 'avm\.(?:res|ptn)' - avm.res & avm.ptn. +Optional. A regex pattern to match against the workflow names. Defaults to 'avm\.(?:res|ptn|utl)' - avm.res, avm.ptn & avm.utl. .PARAMETER ExlcudePattern Optional. A regex pattern that should not match against the workflow names. Defaults to '^$' - empty. @@ -26,7 +26,7 @@ Optional. The GitHub PAT token to use for authentication when interacting with G .EXAMPLE Switch-WorkflowState -RepositoryOwner 'Paul' -RepositoryName 'bicep-registry-modules' -TargetState 'enable' -GitHubToken ('iAmAToken' | ConvertTo-SecureString -AsPlainText -Force) -Enable any AVM res/ptn workflow in the [Paul/bicep-registry-modules] repository that is not in state 'active' using a custom GitHub PAT token. +Enable any AVM res/ptn/utl workflow in the [Paul/bicep-registry-modules] repository that is not in state 'active' using a custom GitHub PAT token. .EXAMPLE Switch-WorkflowState -RepositoryOwner 'Paul' -RepositoryName 'bicep-registry-modules' -TargetState 'disable' @@ -58,7 +58,7 @@ function Switch-WorkflowState { [string] $RepositoryName = 'bicep-registry-modules', [Parameter(Mandatory = $false)] - [string] $IncludePattern = 'avm\.(?:res|ptn)', + [string] $IncludePattern = 'avm\.(?:res|ptn|utl)', [Parameter(Mandatory = $false)] [string] $ExlcudePattern = '^$', diff --git a/avm/utilities/pipelines/platform/Sync-AvmModulesList.ps1 b/avm/utilities/pipelines/platform/Sync-AvmModulesList.ps1 index d7b59763c6..2038ff5d30 100644 --- a/avm/utilities/pipelines/platform/Sync-AvmModulesList.ps1 +++ b/avm/utilities/pipelines/platform/Sync-AvmModulesList.ps1 @@ -33,6 +33,7 @@ function Sync-AvmModulesList { # get CSV data $targetModules = Get-AvmCsvData -ModuleIndex 'Bicep-Resource' | Where-Object { ($_.ModuleStatus -eq 'Available :green_circle:') -or ($_.ModuleStatus -eq 'Orphaned :eyes:') } | Select-Object -ExpandProperty 'ModuleName' | Sort-Object $targetPatterns = Get-AvmCsvData -ModuleIndex 'Bicep-Pattern' | Where-Object { ($_.ModuleStatus -eq 'Available :green_circle:') -or ($_.ModuleStatus -eq 'Orphaned :eyes:') } | Select-Object -ExpandProperty 'ModuleName' | Sort-Object + $targetUtilities = Get-AvmCsvData -ModuleIndex 'Bicep-Utility' | Where-Object { ($_.ModuleStatus -eq 'Available :green_circle:') -or ($_.ModuleStatus -eq 'Orphaned :eyes:') } | Select-Object -ExpandProperty 'ModuleName' | Sort-Object $issueTemplatePath = Join-Path $RepoRoot '.github' 'ISSUE_TEMPLATE' 'avm_module_issue.yml' $issueTemplateContent = Get-Content $issueTemplatePath @@ -51,14 +52,21 @@ function Sync-AvmModulesList { $listedModules = $issueTemplateContent[$startIndex..$endIndex] | Where-Object { -not $_.Contains('#') } | ForEach-Object { $_ -replace '.*- "(avm\/.*)".*', '$1' } | Where-Object { $_ -match 'avm\/res\/.*' } $listedPatterns = $issueTemplateContent[$startIndex..$endIndex] | Where-Object { -not $_.Contains('#') } | ForEach-Object { $_ -replace '.*- "(avm\/.*)".*', '$1' } | Where-Object { $_ -match 'avm\/ptn\/.*' } + $listedUtilities = $issueTemplateContent[$startIndex..$endIndex] | Where-Object { -not $_.Contains('#') } | ForEach-Object { $_ -replace '.*- "(avm\/.*)".*', '$1' } | Where-Object { $_ -match 'avm\/utl\/.*' } $body = '' $missingModules = $targetModules | Where-Object { $listedModules -NotContains $_ } $unexpectedModules = $listedModules | Where-Object { $targetModules -NotContains $_ } - $unexpectedPatterns = $listedPatterns | Where-Object { $targetPatterns -NotContains $_ } + $missingPatterns = $targetPatterns | Where-Object { $listedPatterns -NotContains $_ } + $unexpectedPatterns = $listedPatterns | Where-Object { $targetPatterns -NotContains $_ } + + $missingUtilities = $targetUtilities | Where-Object { $listedUtilities -NotContains $_ } + $unexpectedUtilities = $listedUtilities | Where-Object { $targetUtilities -NotContains $_ } + # Resource modules + # ---------------- if ($missingModules.Count -gt 0) { $body += @" **Missing resource modules:** @@ -77,6 +85,8 @@ $([Environment]::NewLine) "@ } + # Patterns + # -------- if ($missingPatterns.Count -gt 0) { $body += @" **Missing pattern modules:** @@ -95,6 +105,29 @@ $([Environment]::NewLine) "@ } + # Utilities + # --------- + if ($missingUtilities.Count -gt 0) { + $body += @" +**Missing utility modules:** + +$($missingUtilities -join ([Environment]::NewLine)) +$([Environment]::NewLine) +"@ + } + + if ($unexpectedUtilities.Count -gt 0) { + $body += @" +**Unexpected utility modules:** + +$($unexpectedUtilities -join ([Environment]::NewLine)) +$([Environment]::NewLine) +"@ + } + + + # Resource modules + # ---------------- # Should be at correct location $incorrectModuleLines = @() foreach ($finding in (Compare-Object $listedModules ($listedModules | Sort-Object) -SyncWindow 0)) { @@ -113,6 +146,8 @@ $([Environment]::NewLine) "@ } + # Patterns + # -------- $incorrectPatternLines = @() foreach ($finding in (Compare-Object $listedPatterns ($listedPatterns | Sort-Object) -SyncWindow 0)) { if ($finding.SideIndicator -eq '<=') { @@ -130,6 +165,25 @@ $([Environment]::NewLine) "@ } + # Utilities + # --------- + $incorrectUtilityLines = @() + foreach ($finding in (Compare-Object $listedUtilities ($listedUtilities | Sort-Object) -SyncWindow 0)) { + if ($finding.SideIndicator -eq '<=') { + $incorrectUtilityLines += $finding.InputObject + } + } + $incorrectUtilityLines = $incorrectUtilityLines | Sort-Object -Unique + + if ($incorrectUtilityLines.Count -gt 0) { + $body += @" +**Utility modules that are not correctly sorted:** + +$($incorrectUtilityLines -join ([Environment]::NewLine)) +$([Environment]::NewLine) +"@ + } + $issuesFound = $body -ne '' $title = '[AVM core] AVM Module Issue template is not in sync with published resource modules and pattern modules list' @@ -138,7 +192,7 @@ $([Environment]::NewLine) $body = @" > [!IMPORTANT] -> The file [avm_module_issue.yml](https://github.com/Azure/bicep-registry-modules/blob/main/.github/ISSUE_TEMPLATE/avm_module_issue.yml?plain=1) which lists all modules when creating a new issue, is not in sync with the CSV files, that can be found under [resource modules](https://aka.ms/avm/index/bicep/res/csv) and [pattern modules](https://aka.ms/avm/index/bicep/ptn/csv). These CSV files are the single source of truth regarding published modules. Please update the ``avm_module_issue.yml`` accordingly. Please see the following differences that were found. +> The file [avm_module_issue.yml](https://github.com/Azure/bicep-registry-modules/blob/main/.github/ISSUE_TEMPLATE/avm_module_issue.yml?plain=1) which lists all modules when creating a new issue, is not in sync with the CSV files, that can be found under [resource modules](https://aka.ms/avm/index/bicep/res/csv), [pattern modules](https://aka.ms/avm/index/bicep/ptn/csv) and [utility modules](https://aka.ms/avm/index/bicep/utl/csv). These CSV files are the single source of truth regarding published modules. Please update the ``avm_module_issue.yml`` accordingly. Please see the following differences that were found. $([Environment]::NewLine) "@ + $body diff --git a/avm/utilities/pipelines/publish/helper/Get-ModuleReadmeLink.ps1 b/avm/utilities/pipelines/publish/helper/Get-ModuleReadmeLink.ps1 index c4df0f8afc..e4a078e995 100644 --- a/avm/utilities/pipelines/publish/helper/Get-ModuleReadmeLink.ps1 +++ b/avm/utilities/pipelines/publish/helper/Get-ModuleReadmeLink.ps1 @@ -33,6 +33,6 @@ function Get-ModuleReadmeLink { [string] $RegistryBaseUri = 'https://github.com/Azure/bicep-registry-modules/tree' ) - $ModuleRelativeFolderPath = ('avm/{0}' -f ($ModuleFolderPath -split '[\/|\\]avm[\/|\\]')[-1]) -replace '\\', '/' + $ModuleRelativeFolderPath = (($ModuleFolderPath -split '[\/|\\](avm)[\/|\\](res|ptn|utl)[\/|\\]')[-3..-1] -join '/') -replace '\\', '/' return (('{0}/{1}/{2}/README.md' -f $RegistryBaseUri, $TagName, $ModuleRelativeFolderPath) -replace '\\', '/') } diff --git a/avm/utilities/pipelines/publish/helper/Get-ModuleTargetPatchVersion.ps1 b/avm/utilities/pipelines/publish/helper/Get-ModuleTargetPatchVersion.ps1 index 71f76cfe31..b001fdc87c 100644 --- a/avm/utilities/pipelines/publish/helper/Get-ModuleTargetPatchVersion.ps1 +++ b/avm/utilities/pipelines/publish/helper/Get-ModuleTargetPatchVersion.ps1 @@ -38,7 +38,7 @@ function Get-ModuleTargetPatchVersion { [string] $MajMinVersion ) - $ModuleRelativeFolderPath = ('avm/{0}' -f ($ModuleFolderPath -split '[\/|\\]avm[\/|\\]')[-1]) -replace '\\', '/' + $ModuleRelativeFolderPath = (($ModuleFolderPath -split '[\/|\\](avm)[\/|\\](res|ptn|utl)[\/|\\]')[-3..-1] -join '/') -replace '\\', '/' # Get all released module tags (using upstream specifically to work in forks) $existingTagList = git ls-remote --tag 'https://github.com/Azure/bicep-registry-modules.git' "$ModuleRelativeFolderPath/$MajMinVersion*" diff --git a/avm/utilities/pipelines/publish/helper/Get-ModulesToPublish.ps1 b/avm/utilities/pipelines/publish/helper/Get-ModulesToPublish.ps1 index 52c3e0da75..528734d992 100644 --- a/avm/utilities/pipelines/publish/helper/Get-ModulesToPublish.ps1 +++ b/avm/utilities/pipelines/publish/helper/Get-ModulesToPublish.ps1 @@ -84,7 +84,7 @@ function Get-TemplateFileToPublish { [string[]] $PathsToInclude = @() ) - $ModuleRelativeFolderPath = ('avm/{0}' -f ($ModuleFolderPath -split '[\/|\\]avm[\/|\\]')[-1]) -replace '\\', '/' + $ModuleRelativeFolderPath = (($ModuleFolderPath -split '[\/|\\](avm)[\/|\\](res|ptn|utl)[\/|\\]')[-3..-1] -join '/') -replace '\\', '/' $ModifiedFiles = Get-ModifiedFileList -Verbose Write-Verbose "Looking for modified files under: [$ModuleRelativeFolderPath]" -Verbose $modifiedModuleFiles = $ModifiedFiles.FullName | Where-Object { $_ -like "*$ModuleFolderPath*" } @@ -110,7 +110,7 @@ function Get-TemplateFileToPublish { Write-Verbose ('Modified modules found: [{0}]' -f $TemplateFilesToPublish.count) -Verbose $TemplateFilesToPublish | ForEach-Object { - $RelPath = ('avm/{0}' -f ($_ -split '[\/|\\]avm[\/|\\]')[-1]) -replace '\\', '/' + $RelPath = (($_ -split '[\/|\\](avm)[\/|\\](res|ptn|utl)[\/|\\]')[-3..-1] -join '/') -replace '\\', '/' $RelPath = $RelPath.Split('/main.')[0] Write-Verbose " - [$RelPath]" -Verbose } diff --git a/avm/utilities/pipelines/publish/helper/New-ModuleReleaseTag.ps1 b/avm/utilities/pipelines/publish/helper/New-ModuleReleaseTag.ps1 index 1298f449a7..8c82651d84 100644 --- a/avm/utilities/pipelines/publish/helper/New-ModuleReleaseTag.ps1 +++ b/avm/utilities/pipelines/publish/helper/New-ModuleReleaseTag.ps1 @@ -28,7 +28,7 @@ function New-ModuleReleaseTag { [string] $TargetVersion ) - $ModuleRelativeFolderPath = ('avm/{0}' -f ($ModuleFolderPath -split '[\/|\\]avm[\/|\\]')[-1]) -replace '\\', '/' + $ModuleRelativeFolderPath = (($ModuleFolderPath -split '[\/|\\](avm)[\/|\\](res|ptn|utl)[\/|\\]')[-3..-1] -join '/') -replace '\\', '/' # 1 Build Tag $tagName = '{0}/{1}' -f $ModuleRelativeFolderPath, $TargetVersion diff --git a/avm/utilities/pipelines/sharedScripts/Get-BRMRepositoryName.ps1 b/avm/utilities/pipelines/sharedScripts/Get-BRMRepositoryName.ps1 index 27fb9f531c..19de31a1e1 100644 --- a/avm/utilities/pipelines/sharedScripts/Get-BRMRepositoryName.ps1 +++ b/avm/utilities/pipelines/sharedScripts/Get-BRMRepositoryName.ps1 @@ -21,6 +21,6 @@ function Get-BRMRepositoryName { [string] $TemplateFilePath ) - $moduleIdentifier = (Split-Path $TemplateFilePath -Parent) -split '[\/|\\]avm[\/|\\](res|ptn)[\/|\\]' + $moduleIdentifier = (Split-Path $TemplateFilePath -Parent) -split '[\/|\\]avm[\/|\\](res|ptn|utl)[\/|\\]' return ('avm/{0}/{1}' -f $moduleIdentifier[1], $moduleIdentifier[2]) -replace '\\', '/' } diff --git a/avm/utilities/pipelines/sharedScripts/helper/Get-CrossReferencedModuleList.ps1 b/avm/utilities/pipelines/sharedScripts/helper/Get-CrossReferencedModuleList.ps1 index 46ca3bb677..ec32409602 100644 --- a/avm/utilities/pipelines/sharedScripts/helper/Get-CrossReferencedModuleList.ps1 +++ b/avm/utilities/pipelines/sharedScripts/helper/Get-CrossReferencedModuleList.ps1 @@ -148,7 +148,7 @@ function Get-CrossReferencedModuleList { [string] $Path = (Get-Item $PSScriptRoot).Parent.Parent.Parent.Parent ) - $repoRoot = ($Path -split '[\/|\\]{1}avm[\/|\\]{1}')[0] + $repoRoot = ($Path -split '[\/|\\]avm[\/|\\](res|ptn|utl)[\/|\\]')[0] $resultSet = [ordered]@{} # Collect data @@ -176,7 +176,7 @@ function Get-CrossReferencedModuleList { $moduleFolderPath = Split-Path $moduleTemplatePath -Parent ## avm/res// - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' + $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]avm[\/|\\](res|ptn|utl)[\/|\\]')[2] -replace '\\', '/' $providerNamespace = ($resourceTypeIdentifier -split '[\/|\\]')[0] $resourceType = $resourceTypeIdentifier -replace "$providerNamespace[\/|\\]", '' diff --git a/avm/utilities/pipelines/staticValidation/compliance/Set-PesterGitHubOutput.ps1 b/avm/utilities/pipelines/staticValidation/compliance/Set-PesterGitHubOutput.ps1 index a8b4d77db7..83c24536cd 100644 --- a/avm/utilities/pipelines/staticValidation/compliance/Set-PesterGitHubOutput.ps1 +++ b/avm/utilities/pipelines/staticValidation/compliance/Set-PesterGitHubOutput.ps1 @@ -100,7 +100,7 @@ function Set-PesterGitHubOutput { Write-Verbose ('Formatting [{0}] skipped tests' -f $skippedTests.Count) Write-Verbose ('Formatting [{0}] tests with explicit warnings' -f $warnings.Count) - $moduleSplitRegex = '[\/|\\]avm[\/|\\](res|ptn)[\/|\\]' + $moduleSplitRegex = '[\/|\\]avm[\/|\\](res|ptn|utl)[\/|\\]' ###################### # Set output content # diff --git a/avm/utilities/pipelines/staticValidation/compliance/helper/helper.psm1 b/avm/utilities/pipelines/staticValidation/compliance/helper/helper.psm1 index e787148226..8673ec05f4 100644 --- a/avm/utilities/pipelines/staticValidation/compliance/helper/helper.psm1 +++ b/avm/utilities/pipelines/staticValidation/compliance/helper/helper.psm1 @@ -221,7 +221,10 @@ function Resolve-ReadMeParameterList { $parameterSet = @{} - if (-not $Properties) { + if (-not $Properties -and -not $TemplateFileContent.parameters) { + # no Parameters / properties on this level or in the template + return $parameterSet + } elseif (-not $Properties) { # Top-level invocation # Add name as property for later reference $TemplateFileContent.parameters.Keys | ForEach-Object { $TemplateFileContent.parameters[$_]['name'] = $_ } diff --git a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 index 890ff321ba..53a9508953 100644 --- a/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 +++ b/avm/utilities/pipelines/staticValidation/compliance/module.tests.ps1 @@ -22,6 +22,7 @@ $script:MgDeploymentSchema = 'https://schema.management.azure.com/schemas/2019-0 $script:TenantDeploymentSchema = 'https://schema.management.azure.com/schemas/2019-08-01/tenantDeploymentTemplate.json#' $script:telemetryResCsvLink = 'https://aka.ms/avm/index/bicep/res/csv' $script:telemetryPtnCsvLink = 'https://aka.ms/avm/index/bicep/ptn/csv' +$script:telemetryUtlCsvLink = 'https://aka.ms/avm/index/bicep/utl/csv' $script:moduleFolderPaths = $moduleFolderPaths # Shared exception messages @@ -59,11 +60,13 @@ Describe 'File/folder tests' -Tag 'Modules' { $moduleFolderTestCases = [System.Collections.ArrayList] @() foreach ($moduleFolderPath in $moduleFolderPaths) { - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' + $null, $moduleType, $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]avm[\/|\\](res|ptn|utl)[\/|\\]') # 'avm/res|ptn|utl//' would return 'avm', 'res|ptn|utl', '/' + $resourceTypeIdentifier = $resourceTypeIdentifier -replace '\\', '/' $moduleFolderTestCases += @{ moduleFolderName = $resourceTypeIdentifier moduleFolderPath = $moduleFolderPath isTopLevelModule = ($resourceTypeIdentifier -split '[\/|\\]').Count -eq 2 + moduleType = $moduleType } } @@ -100,13 +103,19 @@ Describe 'File/folder tests' -Tag 'Modules' { It '[] Module should contain a [` ORPHANED.md `] file only if orphaned.' -TestCases ($moduleFolderTestCases | Where-Object { $_.isTopLevelModule }) { param( - [string] $moduleFolderPath + [string] $moduleFolderPath, + [string] $moduleType ) $templateFilePath = Join-Path -Path $moduleFolderPath 'main.bicep' # Use correct telemetry link based on file path - $telemetryCsvLink = $moduleFolderPath -match '[\\|\/]res[\\|\/]' ? $telemetryResCsvLink : $telemetryPtnCsvLink + switch ($moduleType) { + 'res' { $telemetryCsvLink = $telemetryResCsvLink; break } + 'ptn' { $telemetryCsvLink = $telemetryPtnCsvLink; break } + 'utl' { $telemetryCsvLink = $telemetryUtlCsvLink; break } + Default {} + } # Fetch CSV # ========= @@ -146,13 +155,13 @@ Describe 'File/folder tests' -Tag 'Modules' { $topLevelModuleTestCases = [System.Collections.ArrayList]@() foreach ($moduleFolderPath in $moduleFolderPaths) { - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' - $moduleTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[1] # 'avm/res|ptn//' would return 'res|ptn' + $null, $moduleType, $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]avm[\/|\\](res|ptn|utl)[\/|\\]') # 'avm/res|ptn|utl//' would return 'avm', 'res|ptn|utl', '/' + $resourceTypeIdentifier = $resourceTypeIdentifier -replace '\\', '/' if (($resourceTypeIdentifier -split '[\/|\\]').Count -eq 2) { $topLevelModuleTestCases += @{ - moduleFolderName = $moduleFolderPath.Replace('\', '/').Split('/avm/')[1] - moduleFolderPath = $moduleFolderPath - moduleTypeIdentifier = $moduleTypeIdentifier + moduleFolderName = $moduleFolderPath.Replace('\', '/').Split('/avm/')[1] + moduleFolderPath = $moduleFolderPath + moduleType = $moduleType } } } @@ -187,7 +196,7 @@ Describe 'File/folder tests' -Tag 'Modules' { $pathExisting | Should -Be $true } - It '[] Module should contain a [` tests/e2e/*waf-aligned `] folder.' -TestCases ($topLevelModuleTestCases | Where-Object { $_.moduleTypeIdentifier -eq 'res' }) { + It '[] Module should contain a [` tests/e2e/*waf-aligned `] folder.' -TestCases ($topLevelModuleTestCases | Where-Object { $_.moduleType -eq 'res' }) { param( [string] $moduleFolderPath @@ -197,7 +206,7 @@ Describe 'File/folder tests' -Tag 'Modules' { $wafAlignedFolder | Should -Not -BeNullOrEmpty } - It '[] Module should contain a [` tests/e2e/*defaults `] folder.' -TestCases ($topLevelModuleTestCases | Where-Object { $_.moduleTypeIdentifier -eq 'res' }) { + It '[] Module should contain a [` tests/e2e/*defaults `] folder.' -TestCases ($topLevelModuleTestCases | Where-Object { $_.moduleType -eq 'res' }) { param( [string] $moduleFolderPath @@ -235,8 +244,8 @@ Describe 'Pipeline tests' -Tag 'Pipeline' { $pipelineTestCases = [System.Collections.ArrayList] @() foreach ($moduleFolderPath in $moduleFolderPaths) { - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' - $relativeModulePath = Join-Path 'avm' ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}')[1] + $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]avm[\/|\\](res|ptn|utl)[\/|\\]')[2] -replace '\\', '/' # 'avm/res|ptn|utl//' would return '/' + $relativeModulePath = Join-Path 'avm' ($moduleFolderPath -split '[\/|\\]avm[\/|\\]')[1] $isTopLevelModule = ($resourceTypeIdentifier -split '[\/|\\]').Count -eq 2 if ($isTopLevelModule) { @@ -290,7 +299,7 @@ Describe 'Module tests' -Tag 'Module' { foreach ($moduleFolderPath in $moduleFolderPaths) { - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' + $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]avm[\/|\\](res|ptn|utl)[\/|\\]')[2] -replace '\\', '/' # 'avm/res|ptn|utl//' would return '/' $templateFilePath = Join-Path $moduleFolderPath 'main.bicep' $readmeFileTestCases += @{ @@ -348,7 +357,7 @@ Describe 'Module tests' -Tag 'Module' { continue } - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' + $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]avm[\/|\\](res|ptn|utl)[\/|\\]')[2] -replace '\\', '/' # 'avm/res|ptn|utl//' would return '/' $armTemplateTestCases += @{ moduleFolderName = $resourceTypeIdentifier @@ -385,7 +394,7 @@ Describe 'Module tests' -Tag 'Module' { $newJson = ConvertTo-OrderedHashtable -JSONInputObject (ConvertTo-Json $newJson -Depth 99) # compare - (ConvertTo-Json $originalJson -Depth 99) | Should -Be (ConvertTo-Json $newJson -Depth 99) -Because "the [$moduleFolderName] [main.json] should be based on the latest [main.bicep] file. Please run [` bicep build >bicepFilePath< `] using the latest Bicep CLI version." + (ConvertTo-Json $originalJson -Depth 99) | Should -Be (ConvertTo-Json $newJson -Depth 99) -Because "the [$moduleFolderName] [main.json] should be based on the latest [main.bicep] file. Please run [` bicep build >bicepFilePath< `] using the latest Bicep CLI version." # Reset template file to original state git checkout HEAD -- $armTemplatePath @@ -400,7 +409,8 @@ Describe 'Module tests' -Tag 'Module' { $templateFilePath = Join-Path $moduleFolderPath 'main.bicep' $templateFileContent = $builtTestFileMap[$templateFilePath] - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' + $null, $moduleType, $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]avm[\/|\\](res|ptn|utl)[\/|\\]') # 'avm/res|ptn|utl//' would return 'avm', 'res|ptn|utl', '/' + $resourceTypeIdentifier = $resourceTypeIdentifier -replace '\\', '/' # Test file setup $moduleFolderTestCases += @{ @@ -410,6 +420,7 @@ Describe 'Module tests' -Tag 'Module' { templateFileParameters = Resolve-ReadMeParameterList -TemplateFileContent $templateFileContent readMeFilePath = Join-Path (Split-Path $templateFilePath) 'README.md' isTopLevelModule = ($resourceTypeIdentifier -split '[\/|\\]').Count -eq 2 + moduleType = $moduleType } } @@ -453,7 +464,7 @@ Describe 'Module tests' -Tag 'Module' { [hashtable] $templateFileContent ) $Schemaverion = $templateFileContent.'$schema' - ($Schemaverion.Substring(0, 5) -eq 'https') | Should -Be $true + ($Schemaverion.Substring(0, 5) -eq 'https') | Should -Be $true } It '[] The template file should contain required elements [schema], [contentVersion], [resources].' -TestCases $moduleFolderTestCases { @@ -509,7 +520,8 @@ Describe 'Module tests' -Tag 'Module' { } } - It '[] The telemetry parameter should be present & have the expected type, default value & metadata description.' -TestCases ($moduleFolderTestCases | Where-Object { $_.isTopLevelModule }) { + # If any resources in the module are deployed, a telemetry deployment should be carried out as well + It '[] The telemetry parameter should be present & have the expected type, default value & metadata description.' -TestCases ($moduleFolderTestCases | Where-Object { $_.isTopLevelModule -and $_.templateFileContent.resources.count -gt 0 }) { param( [hashtable] $templateFileParameters @@ -633,12 +645,13 @@ Describe 'Module tests' -Tag 'Module' { $udtSpecificTestCases = [System.Collections.ArrayList] @() # Specific UDT test cases for singular UDTs (e.g. tags) foreach ($moduleFolderPath in $moduleFolderPaths) { - if ($moduleFolderPath -match '[\/|\\]avm[\/|\\]ptn[\/|\\]') { - # Skip UDT interface tests for ptn modules + if ($moduleFolderPath -match '[\/|\\]avm[\/|\\](ptn|utl)[\/|\\]') { + # Skip UDT interface tests for ptn & utl modules continue } - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' + $null, $moduleType, $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]avm[\/|\\](res|ptn|utl)[\/|\\]') # 'avm/res|ptn|utl//' would return 'avm', 'res|ptn|utl', '/' + $resourceTypeIdentifier = $resourceTypeIdentifier -replace '\\', '/' $templateFilePath = Join-Path $moduleFolderPath 'main.bicep' $templateFileContent = $builtTestFileMap[$templateFilePath] @@ -646,6 +659,7 @@ Describe 'Module tests' -Tag 'Module' { moduleFolderName = $resourceTypeIdentifier templateFileContent = $templateFileContent templateFileContentBicep = Get-Content $templateFilePath + moduleType = $moduleType } # Setting expected URL only for those that doen't have multiple different variants @@ -832,7 +846,8 @@ Describe 'Module tests' -Tag 'Module' { } Context 'Resources' { - It '[] Telemetry deployment should be present in the template.' -TestCases ($moduleFolderTestCases | Where-Object { $_.isTopLevelModule }) { + # If any resources in the module are deployed, a telemetry deployment should be carried out as well + It '[] Telemetry deployment should be present in the template.' -TestCases ($moduleFolderTestCases | Where-Object { $_.isTopLevelModule -and $_.templateFileContent.resources.count -gt 0 }) { param( [hashtable] $templateFileContent @@ -865,7 +880,7 @@ Describe 'Module tests' -Tag 'Module' { $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '*46d3xbcp*' } # The AVM telemetry prefix if (-not $telemetryDeployment) { - Set-ItResult -Skipped -Because 'Skipping this test as telemetry was not implemented in template' + Set-ItResult -Skipped -Because 'telemetry was not implemented in template' return } @@ -888,7 +903,7 @@ Describe 'Module tests' -Tag 'Module' { $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '*46d3xbcp*' } # The AVM telemetry prefix if (-not $telemetryDeployment) { - Set-ItResult -Skipped -Because 'Skipping this test as telemetry was not implemented in template' + Set-ItResult -Skipped -Because 'telemetry was not implemented in template' return } @@ -900,11 +915,31 @@ Describe 'Module tests' -Tag 'Module' { param( [string] $templateFilePath, + [string] $moduleType, [hashtable] $templateFileContent ) + # With the introduction of user defined types, the way resources are configured in the schema slightly changed. We have to account for that. + if ($templateFileContent.resources.GetType().Name -eq 'Object[]') { + $templateResources = $templateFileContent.resources + } else { + $templateResources = $templateFileContent.resources.Keys | ForEach-Object { $templateFileContent.resources[$_] } + } + + $telemetryDeployment = $templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '*46d3xbcp*' } # The AVM telemetry prefix + + if (-not $telemetryDeployment) { + Set-ItResult -Skipped -Because 'telemetry was not implemented in template' + return + } + # Use correct telemetry link based on file path - $telemetryCsvLink = $templateFilePath -match '[\\|\/]res[\\|\/]' ? $telemetryResCsvLink : $telemetryPtnCsvLink + switch ($moduleType) { + 'res' { $telemetryCsvLink = $telemetryResCsvLink; break } + 'ptn' { $telemetryCsvLink = $telemetryPtnCsvLink; break } + 'utl' { $telemetryCsvLink = $telemetryUtlCsvLink; break } + Default {} + } # Fetch CSV # ========= @@ -933,13 +968,8 @@ Describe 'Module tests' -Tag 'Module' { # Collect resource & compare # ========================== - # With the introduction of user defined types, the way resources are configured in the schema slightly changed. We have to account for that. - if ($templateFileContent.resources.GetType().Name -eq 'Object[]') { - $templateResources = $templateFileContent.resources - } else { - $templateResources = $templateFileContent.resources.Keys | ForEach-Object { $templateFileContent.resources[$_] } - } - $telemetryDeploymentName = ($templateResources | Where-Object { $_.condition -like '*telemetry*' -and $_.name -like '*46d3xbcp*' }).name # The AVM telemetry prefix + + $telemetryDeploymentName = $telemetryDeployment.name # The AVM telemetry prefix $telemetryDeploymentName | Should -Match "$expectedTelemetryIdentifier" } } @@ -1016,6 +1046,11 @@ Describe 'Module tests' -Tag 'Module' { [string] $templateFilePath ) + if ($templateFileContent.resources.count -eq 0) { + Set-ItResult -Skipped -Because 'with no resources are deployed in the template, the test is not required' + return + } + $outputs = $templateFileContent.outputs.Keys $deploymentScope = Get-ScopeOfTemplateFile -TemplateFilePath $templateFilePath @@ -1122,8 +1157,9 @@ Describe 'Governance tests' { $governanceTestCases = [System.Collections.ArrayList] @() foreach ($moduleFolderPath in $moduleFolderPaths) { - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' - $relativeModulePath = Join-Path 'avm' ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}')[1] + $null, $moduleType, $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]avm[\/|\\](res|ptn|utl)[\/|\\]') # 'avm/res|ptn|utl//' would return 'avm', 'res|ptn|utl', '/' + $resourceTypeIdentifier = $resourceTypeIdentifier -replace '\\', '/' + $relativeModulePath = Join-Path 'avm' ($moduleFolderPath -split '[\/|\\]avm[\/|\\]')[1] $isTopLevelModule = ($resourceTypeIdentifier -split '[\/|\\]').Count -eq 2 if ($isTopLevelModule) { @@ -1132,6 +1168,7 @@ Describe 'Governance tests' { relativeModulePath = $relativeModulePath repoRootPath = $repoRootPath moduleFolderName = $resourceTypeIdentifier + moduleType = $moduleType } } } @@ -1213,13 +1250,16 @@ Describe 'Test file tests' -Tag 'TestTemplate' { $testFilePaths = (Get-ChildItem -Path $moduleFolderPath -Recurse -Filter 'main.test.bicep').FullName | Sort-Object -Culture 'en-US' foreach ($testFilePath in $testFilePaths) { $testFileContent = Get-Content $testFilePath - $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]{1}avm[\/|\\]{1}(res|ptn)[\/|\\]{1}')[2] -replace '\\', '/' # 'avm/res|ptn//' would return '/' + $null, $moduleType, $resourceTypeIdentifier = ($moduleFolderPath -split '[\/|\\]avm[\/|\\](res|ptn|utl)[\/|\\]') # 'avm/res|ptn|utl//' would return 'avm', 'res|ptn|utl', '/' + $resourceTypeIdentifier = $resourceTypeIdentifier -replace '\\', '/' $deploymentTestFileTestCases += @{ - testName = Split-Path (Split-Path $testFilePath) -Leaf - testFilePath = $testFilePath - testFileContent = $testFileContent - moduleFolderName = $resourceTypeIdentifier + testName = Split-Path (Split-Path $testFilePath) -Leaf + testFilePath = $testFilePath + testFileContent = $testFileContent + compiledTestFileContent = $builtTestFileMap[$testFilePath] + moduleFolderName = $resourceTypeIdentifier + moduleType = $moduleType } } } @@ -1228,9 +1268,16 @@ Describe 'Test file tests' -Tag 'TestTemplate' { It '[] Bicep test deployment files should contain a parameter [serviceShort] for test case []' -TestCases $deploymentTestFileTestCases { param( - [object[]] $testFileContent + [object[]] $testFileContent, + [hashtable] $compiledTestFileContent ) - ($testFileContent -match "^param serviceShort string = '(.*)$") | Should -Not -BeNullOrEmpty -Because 'the module test deployment file should contain a parameter [serviceShort] using the syntax [param serviceShort string = ''*''].' + + if ($compiledTestFileContent.resources.count -eq 0) { + Set-ItResult -Skipped -Because 'with no deployments in the test file, the test is not required.' + return + } + + ($testFileContent -match "^param serviceShort string = '(.*)$") | Should -Not -BeNullOrEmpty -Because 'the module test deployment file should contain a parameter [serviceShort] using the syntax [param serviceShort string = ''*''].' } It '[] [] Bicep test deployment files in a [defaults] folder should have a parameter [serviceShort] with a value ending with [min]' -TestCases ($deploymentTestFileTestCases | Where-Object { $_.testFilePath -match '.*[\\|\/](.+\.)?defaults[\\|\/].*' }) { @@ -1277,7 +1324,7 @@ Describe 'Test file tests' -Tag 'TestTemplate' { param( [object[]] $testFileContent ) - ($testFileContent | Out-String) | Should -Match 'metadata name = .+' -Because 'Test cases should contain a metadata string [name] in the format `metadata name = ''One cake of a name''` to be more descriptive. If provided, the tooling will automatically inject it into the module''s readme.md file.' + ($testFileContent | Out-String) | Should -Match 'metadata name = .+' -Because 'Test cases should contain a metadata string [name] in the format `metadata name = ''One cake of a name''` to be more descriptive. If provided, the tooling will automatically inject it into the module''s readme.md file.' } It '[] Bicep test deployment files should contain a metadata string [description] for test case []' -TestCases $deploymentTestFileTestCases { @@ -1285,24 +1332,36 @@ Describe 'Test file tests' -Tag 'TestTemplate' { param( [object[]] $testFileContent ) - ($testFileContent | Out-String) | Should -Match 'metadata description = .+' -Because 'Test cases should contain a metadata string [description] in the format `metadata description = ''The cake is a lie''` to be more descriptive. If provided, the tooling will automatically inject it into the module''s readme.md file.' + ($testFileContent | Out-String) | Should -Match 'metadata description = .+' -Because 'Test cases should contain a metadata string [description] in the format `metadata description = ''The cake is a lie''` to be more descriptive. If provided, the tooling will automatically inject it into the module''s readme.md file.' } It "[] Bicep test deployment files should contain a parameter [namePrefix] with value ['#_namePrefix_#'] for test case []" -TestCases $deploymentTestFileTestCases { param( - [object[]] $testFileContent + [object[]] $testFileContent, + [hashtable] $compiledTestFileContent ) - ($testFileContent | Out-String) | Should -Match "param namePrefix string = '#_namePrefix_#'" -Because 'The test CI needs this value to ensure that deployed resources have unique names per fork.' + if ($compiledTestFileContent.resources.count -eq 0) { + Set-ItResult -Skipped -Because 'without deployments in the test file, the test is not required.' + return + } + + ($testFileContent | Out-String) | Should -Match "param namePrefix string = '#_namePrefix_#'" -Because 'The test CI needs this value to ensure that deployed resources have unique names per fork.' } It "[] Bicep test deployment files should invoke test like [`module testDeployment '../.*main.bicep' = [ or {`] for test case []" -TestCases $deploymentTestFileTestCases { param( - [object[]] $testFileContent + [object[]] $testFileContent, + [hashtable] $compiledTestFileContent ) + if ($compiledTestFileContent.resources.count -eq 0) { + Set-ItResult -Skipped -Because 'without deployments in the test file, the test is not required.' + return + } + $testIndex = ($testFileContent | Select-String ("^module testDeployment '..\/.*main.bicep' = .*[\[|\{]$") | ForEach-Object { $_.LineNumber - 1 })[0] $testIndex -ne -1 | Should -Be $true -Because 'the module test invocation should be in the expected format to allow identification.' @@ -1311,23 +1370,19 @@ Describe 'Test file tests' -Tag 'TestTemplate' { It '[] Bicep test deployment name should contain [`-test-`] for test case []' -TestCases $deploymentTestFileTestCases { param( - [object[]] $testFileContent + [object[]] $testFileContent, + [hashtable] $compiledTestFileContent ) + if ($compiledTestFileContent.resources.count -eq 0) { + Set-ItResult -Skipped -Because 'without deployments in the test file, the test is not required.' + return + } + $expectedNameFormat = ($testFileContent | Out-String) -match '\s*name:.+-test-.+\s*' $expectedNameFormat | Should -Be $true -Because 'the handle ''-test-'' should be part of the module test invocation''s resource name to allow identification.' } - - It '[] Bicep test deployment should have parameter [`serviceShort`] for test case []' -TestCases $deploymentTestFileTestCases { - - param( - [object[]] $testFileContent - ) - - $hasExpectedParam = ($testFileContent | Out-String) -match '\s*param\s+serviceShort\s+string\s*' - $hasExpectedParam | Should -Be $true - } } } From 7b37781d52b7c2f7e6da7f5ac946e8bf06b48c00 Mon Sep 17 00:00:00 2001 From: Chinedum Echeta <60179183+cecheta@users.noreply.github.com> Date: Tue, 10 Sep 2024 08:15:14 +0100 Subject: [PATCH 10/57] fix: `avm/res/machine-learning-services/workspace` Enforce location for AI Services (#3151) ## Description Hardcodes region when deploying AI Services, as not available in East Asia. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.machine-learning-services.workspace](https://github.com/cecheta/bicep-registry-modules/actions/workflows/avm.res.machine-learning-services.workspace.yml/badge.svg?branch=ai-region)](https://github.com/cecheta/bicep-registry-modules/actions/workflows/avm.res.machine-learning-services.workspace.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [x] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../workspace/tests/e2e/ai/main.test.bicep | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/avm/res/machine-learning-services/workspace/tests/e2e/ai/main.test.bicep b/avm/res/machine-learning-services/workspace/tests/e2e/ai/main.test.bicep index 8ea4b81b79..927fe46d6c 100644 --- a/avm/res/machine-learning-services/workspace/tests/e2e/ai/main.test.bicep +++ b/avm/res/machine-learning-services/workspace/tests/e2e/ai/main.test.bicep @@ -11,9 +11,6 @@ metadata description = 'This instance deploys an Azure AI hub workspace.' @maxLength(90) param resourceGroupName string = 'dep-${namePrefix}-machinelearningservices.workspaces-${serviceShort}-rg' -@description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location - @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') param serviceShort string = 'mlswai' @@ -23,6 +20,10 @@ param baseTime string = utcNow('u') @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +// AI Services not available in all regions +#disable-next-line no-hardcoded-location +var enforcedLocation = 'uksouth' + // ============ // // Dependencies // // ============ // @@ -31,19 +32,19 @@ param namePrefix string = '#_namePrefix_#' // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: resourceGroupName - location: resourceLocation + location: enforcedLocation } module nestedDependencies 'dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' params: { keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}-${substring(uniqueString(baseTime), 0, 3)}' applicationInsightsName: 'dep-${namePrefix}-appI-${serviceShort}' storageAccountName: 'dep${namePrefix}st${serviceShort}' secondaryStorageAccountName: 'dep${namePrefix}st${serviceShort}2' aiServicesName: 'dep-${namePrefix}-ai-${serviceShort}' - location: resourceLocation + location: enforcedLocation } } @@ -55,10 +56,10 @@ module nestedDependencies 'dependencies.bicep' = { module testDeployment '../../../main.bicep' = [ for iteration in ['init', 'idem']: { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}001' - location: resourceLocation + location: enforcedLocation associatedApplicationInsightsResourceId: nestedDependencies.outputs.applicationInsightsResourceId associatedKeyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId associatedStorageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId @@ -78,7 +79,7 @@ module testDeployment '../../../main.bicep' = [ metadata: { ApiType: 'Azure' ResourceId: nestedDependencies.outputs.aiServicesResourceId - Location: resourceLocation + Location: enforcedLocation ApiVersion: '2023-07-01-preview' DeploymentApiVersion: '2023-10-01-preview' } @@ -99,10 +100,10 @@ module testDeployment '../../../main.bicep' = [ module testProjectDeployment '../../../main.bicep' = [ for (iteration, i) in ['init', 'idem']: { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-proj-${iteration}' + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}-proj-${iteration}' params: { name: '${namePrefix}${serviceShort}002' - location: resourceLocation + location: enforcedLocation sku: 'Basic' kind: 'Project' hubResourceId: testDeployment[i].outputs.resourceId From e5ac2f62b5e0a180c5cc76db0e75c3744badb116 Mon Sep 17 00:00:00 2001 From: Clint Grove Date: Tue, 10 Sep 2024 15:01:15 +0300 Subject: [PATCH 11/57] feat: Add defaultCatalog `avm/res/databricks/workspace` (#3178) ## Description Fixes #3087 Closes #3087 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.databricks.workspace](https://github.com/clintgrove/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml/badge.svg?branch=3807-issue-addcatalog)](https://github.com/clintgrove/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [x] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/databricks/workspace/README.md | 36 +++++++++++++++++++ avm/res/databricks/workspace/main.bicep | 20 +++++++++++ avm/res/databricks/workspace/main.json | 26 ++++++++++++-- .../workspace/tests/e2e/max/main.test.bicep | 4 +++ avm/res/databricks/workspace/version.json | 2 +- 5 files changed, 85 insertions(+), 3 deletions(-) diff --git a/avm/res/databricks/workspace/README.md b/avm/res/databricks/workspace/README.md index 6ee2ca03c2..b3084f34a5 100644 --- a/avm/res/databricks/workspace/README.md +++ b/avm/res/databricks/workspace/README.md @@ -112,6 +112,9 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { customPrivateSubnetName: '' customPublicSubnetName: '' customVirtualNetworkResourceId: '' + defaultCatalog: { + initialType: 'UnityCatalog' + } diagnosticSettings: [ { eventHubAuthorizationRuleResourceId: '' @@ -246,6 +249,11 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { "customVirtualNetworkResourceId": { "value": "" }, + "defaultCatalog": { + "value": { + "initialType": "UnityCatalog" + } + }, "diagnosticSettings": { "value": [ { @@ -677,6 +685,7 @@ module workspace 'br/public:avm/res/databricks/workspace:' = { | [`customPrivateSubnetName`](#parameter-customprivatesubnetname) | string | The name of the Private Subnet within the Virtual Network. | | [`customPublicSubnetName`](#parameter-custompublicsubnetname) | string | The name of a Public Subnet within the Virtual Network. | | [`customVirtualNetworkResourceId`](#parameter-customvirtualnetworkresourceid) | string | The resource ID of a Virtual Network where this Databricks Cluster should be created. | +| [`defaultCatalog`](#parameter-defaultcatalog) | object | The default catalog configuration for the Databricks workspace. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`disablePublicIp`](#parameter-disablepublicip) | bool | Disable Public IP. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | @@ -854,6 +863,33 @@ The resource ID of a Virtual Network where this Databricks Cluster should be cre - Type: string - Default: `''` +### Parameter: `defaultCatalog` + +The default catalog configuration for the Databricks workspace. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`initialType`](#parameter-defaultcataloginitialtype) | string | Choose between HiveMetastore or UnityCatalog. | + +### Parameter: `defaultCatalog.initialType` + +Choose between HiveMetastore or UnityCatalog. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'HiveMetastore' + 'UnityCatalog' + ] + ``` + ### Parameter: `diagnosticSettings` The diagnostic settings of the service. diff --git a/avm/res/databricks/workspace/main.bicep b/avm/res/databricks/workspace/main.bicep index 152811530e..38f9840878 100644 --- a/avm/res/databricks/workspace/main.bicep +++ b/avm/res/databricks/workspace/main.bicep @@ -113,6 +113,9 @@ param storageAccountPrivateEndpoints privateEndpointType @description('Conditional. The resource ID of the associated access connector for private access to the managed workspace storage account. Required if privateStorageAccount is enabled.') param accessConnectorResourceId string = '' +@description('Optional. The default catalog configuration for the Databricks workspace.') +param defaultCatalog defaultCatalogType? + var builtInRoleNames = { Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') @@ -328,6 +331,14 @@ resource workspace 'Microsoft.Databricks/workspaces@2024-05-01' = { identityType: 'SystemAssigned' } } + : {}, + !empty(defaultCatalog) + ? { + defaultCatalog: { + initialName: '' + initialType: defaultCatalog.?initialType + } + } : {} ) } @@ -740,3 +751,12 @@ type diagnosticSettingType = { @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type defaultCatalogType = { + //This value cannot be set to a custom value. Reason --> 'InvalidInitialCatalogName' message: 'Currently custom initial catalog name is not supported. This capability will be added in future.' + //@description('Optional. Set the name of the Catalog.') + //initialName: '' + + @description('Required. Choose between HiveMetastore or UnityCatalog.') + initialType: 'HiveMetastore' | 'UnityCatalog' +} diff --git a/avm/res/databricks/workspace/main.json b/avm/res/databricks/workspace/main.json index 8579f7478a..2f4f7b5b64 100644 --- a/avm/res/databricks/workspace/main.json +++ b/avm/res/databricks/workspace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "6248899045759773040" + "templateHash": "8011778884263076951" }, "name": "Azure Databricks Workspaces", "description": "This module deploys an Azure Databricks Workspace.", @@ -495,6 +495,21 @@ } }, "nullable": true + }, + "defaultCatalogType": { + "type": "object", + "properties": { + "initialType": { + "type": "string", + "allowedValues": [ + "HiveMetastore", + "UnityCatalog" + ], + "metadata": { + "description": "Required. Choose between HiveMetastore or UnityCatalog." + } + } + } } }, "parameters": { @@ -724,6 +739,13 @@ "metadata": { "description": "Conditional. The resource ID of the associated access connector for private access to the managed workspace storage account. Required if privateStorageAccount is enabled." } + }, + "defaultCatalog": { + "$ref": "#/definitions/defaultCatalogType", + "nullable": true, + "metadata": { + "description": "Optional. The default catalog configuration for the Databricks workspace." + } } }, "variables": { @@ -814,7 +836,7 @@ "sku": { "name": "[parameters('skuName')]" }, - "properties": "[union(createObject('managedResourceGroupId', if(not(empty(parameters('managedResourceGroupResourceId'))), parameters('managedResourceGroupResourceId'), format('{0}/resourceGroups/rg-{1}-managed', subscription().id, parameters('name'))), 'parameters', union(createObject('enableNoPublicIp', createObject('value', parameters('disablePublicIp')), 'prepareEncryption', createObject('value', parameters('prepareEncryption')), 'vnetAddressPrefix', createObject('value', parameters('vnetAddressPrefix')), 'requireInfrastructureEncryption', createObject('value', parameters('requireInfrastructureEncryption'))), if(not(empty(parameters('customVirtualNetworkResourceId'))), createObject('customVirtualNetworkId', createObject('value', parameters('customVirtualNetworkResourceId'))), createObject()), if(not(empty(parameters('amlWorkspaceResourceId'))), createObject('amlWorkspaceId', createObject('value', parameters('amlWorkspaceResourceId'))), createObject()), if(not(empty(parameters('customPrivateSubnetName'))), createObject('customPrivateSubnetName', createObject('value', parameters('customPrivateSubnetName'))), createObject()), if(not(empty(parameters('customPublicSubnetName'))), createObject('customPublicSubnetName', createObject('value', parameters('customPublicSubnetName'))), createObject()), if(not(empty(parameters('loadBalancerBackendPoolName'))), createObject('loadBalancerBackendPoolName', createObject('value', parameters('loadBalancerBackendPoolName'))), createObject()), if(not(empty(parameters('loadBalancerResourceId'))), createObject('loadBalancerId', createObject('value', parameters('loadBalancerResourceId'))), createObject()), if(not(empty(parameters('natGatewayName'))), createObject('natGatewayName', createObject('value', parameters('natGatewayName'))), createObject()), if(not(empty(parameters('publicIpName'))), createObject('publicIpName', createObject('value', parameters('publicIpName'))), createObject()), if(not(empty(parameters('storageAccountName'))), createObject('storageAccountName', createObject('value', parameters('storageAccountName'))), createObject()), if(not(empty(parameters('storageAccountSkuName'))), createObject('storageAccountSkuName', createObject('value', parameters('storageAccountSkuName'))), createObject())), 'publicNetworkAccess', parameters('publicNetworkAccess'), 'requiredNsgRules', parameters('requiredNsgRules'), 'encryption', if(or(not(empty(parameters('customerManagedKey'))), not(empty(parameters('customerManagedKeyManagedDisk')))), createObject('entities', createObject('managedServices', if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null()), 'managedDisk', if(not(empty(parameters('customerManagedKeyManagedDisk'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKManagedDiskKeyVault').vaultUri, 'keyName', parameters('customerManagedKeyManagedDisk').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVersion'), ''))), parameters('customerManagedKeyManagedDisk').keyVersion, last(split(reference('cMKManagedDiskKeyVault::cMKKey').keyUriWithVersion, '/')))), 'rotationToLatestKeyVersionEnabled', coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'rotationToLatestKeyVersionEnabled'), true())), null()))), null())), if(not(empty(parameters('privateStorageAccount'))), createObject('defaultStorageFirewall', parameters('privateStorageAccount'), 'accessConnector', createObject('id', parameters('accessConnectorResourceId'), 'identityType', 'SystemAssigned')), createObject()))]", + "properties": "[union(createObject('managedResourceGroupId', if(not(empty(parameters('managedResourceGroupResourceId'))), parameters('managedResourceGroupResourceId'), format('{0}/resourceGroups/rg-{1}-managed', subscription().id, parameters('name'))), 'parameters', union(createObject('enableNoPublicIp', createObject('value', parameters('disablePublicIp')), 'prepareEncryption', createObject('value', parameters('prepareEncryption')), 'vnetAddressPrefix', createObject('value', parameters('vnetAddressPrefix')), 'requireInfrastructureEncryption', createObject('value', parameters('requireInfrastructureEncryption'))), if(not(empty(parameters('customVirtualNetworkResourceId'))), createObject('customVirtualNetworkId', createObject('value', parameters('customVirtualNetworkResourceId'))), createObject()), if(not(empty(parameters('amlWorkspaceResourceId'))), createObject('amlWorkspaceId', createObject('value', parameters('amlWorkspaceResourceId'))), createObject()), if(not(empty(parameters('customPrivateSubnetName'))), createObject('customPrivateSubnetName', createObject('value', parameters('customPrivateSubnetName'))), createObject()), if(not(empty(parameters('customPublicSubnetName'))), createObject('customPublicSubnetName', createObject('value', parameters('customPublicSubnetName'))), createObject()), if(not(empty(parameters('loadBalancerBackendPoolName'))), createObject('loadBalancerBackendPoolName', createObject('value', parameters('loadBalancerBackendPoolName'))), createObject()), if(not(empty(parameters('loadBalancerResourceId'))), createObject('loadBalancerId', createObject('value', parameters('loadBalancerResourceId'))), createObject()), if(not(empty(parameters('natGatewayName'))), createObject('natGatewayName', createObject('value', parameters('natGatewayName'))), createObject()), if(not(empty(parameters('publicIpName'))), createObject('publicIpName', createObject('value', parameters('publicIpName'))), createObject()), if(not(empty(parameters('storageAccountName'))), createObject('storageAccountName', createObject('value', parameters('storageAccountName'))), createObject()), if(not(empty(parameters('storageAccountSkuName'))), createObject('storageAccountSkuName', createObject('value', parameters('storageAccountSkuName'))), createObject())), 'publicNetworkAccess', parameters('publicNetworkAccess'), 'requiredNsgRules', parameters('requiredNsgRules'), 'encryption', if(or(not(empty(parameters('customerManagedKey'))), not(empty(parameters('customerManagedKeyManagedDisk')))), createObject('entities', createObject('managedServices', if(not(empty(parameters('customerManagedKey'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKKeyVault').vaultUri, 'keyName', parameters('customerManagedKey').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/'))))), null()), 'managedDisk', if(not(empty(parameters('customerManagedKeyManagedDisk'))), createObject('keySource', 'Microsoft.Keyvault', 'keyVaultProperties', createObject('keyVaultUri', reference('cMKManagedDiskKeyVault').vaultUri, 'keyName', parameters('customerManagedKeyManagedDisk').keyName, 'keyVersion', if(not(empty(coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'keyVersion'), ''))), parameters('customerManagedKeyManagedDisk').keyVersion, last(split(reference('cMKManagedDiskKeyVault::cMKKey').keyUriWithVersion, '/')))), 'rotationToLatestKeyVersionEnabled', coalesce(tryGet(parameters('customerManagedKeyManagedDisk'), 'rotationToLatestKeyVersionEnabled'), true())), null()))), null())), if(not(empty(parameters('privateStorageAccount'))), createObject('defaultStorageFirewall', parameters('privateStorageAccount'), 'accessConnector', createObject('id', parameters('accessConnectorResourceId'), 'identityType', 'SystemAssigned')), createObject()), if(not(empty(parameters('defaultCatalog'))), createObject('defaultCatalog', createObject('initialName', '', 'initialType', tryGet(parameters('defaultCatalog'), 'initialType'))), createObject()))]", "dependsOn": [ "cMKKeyVault", "cMKManagedDiskKeyVault" diff --git a/avm/res/databricks/workspace/tests/e2e/max/main.test.bicep b/avm/res/databricks/workspace/tests/e2e/max/main.test.bicep index b1745bfe1d..3ee2aeb6e5 100644 --- a/avm/res/databricks/workspace/tests/e2e/max/main.test.bicep +++ b/avm/res/databricks/workspace/tests/e2e/max/main.test.bicep @@ -188,6 +188,10 @@ module testDeployment '../../../main.bicep' = [ managedResourceGroupResourceId: '${subscription().id}/resourceGroups/rg-${resourceGroupName}-managed' requireInfrastructureEncryption: true vnetAddressPrefix: '10.100' + defaultCatalog: { + //initialName: '' Cannot be set to anything other than an empty string. {"code":"InvalidInitialCatalogName","message":"Currently custom initial catalog name is not supported. This capability will be added in future."} + initialType: 'UnityCatalog' // Choose between 'HiveCatalog' OR 'UnityCatalog' + } } dependsOn: [ nestedDependencies diff --git a/avm/res/databricks/workspace/version.json b/avm/res/databricks/workspace/version.json index 0f81d22abc..e0a6c494b9 100644 --- a/avm/res/databricks/workspace/version.json +++ b/avm/res/databricks/workspace/version.json @@ -4,4 +4,4 @@ "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} From 4ca0ccdedfb5c8f7443af708aa33907d7751164a Mon Sep 17 00:00:00 2001 From: "Jianing Wang (MSFT)" <141212663+jianingwang123@users.noreply.github.com> Date: Tue, 10 Sep 2024 20:12:09 +0800 Subject: [PATCH 12/57] feat: Add new ptn modules `azd/apim-api` (#3206) ## Description Fixes [Azure/Azure-Verified-Modules/issues#1232](https://github.com/Azure/Azure-Verified-Modules/issues/1232) ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.azd.apim-api](https://github.com/jianingwang123/bicep-registry-modules/actions/workflows/avm.ptn.azd.apim-api.yml/badge.svg?branch=apim-api)](https://github.com/jianingwang123/bicep-registry-modules/actions/workflows/avm.ptn.azd.apim-api.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings @jongio for notification. --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + .github/workflows/avm.ptn.azd.apim-api.yml | 88 +++++ avm/ptn/azd/apim-api/README.md | 217 ++++++++++++ avm/ptn/azd/apim-api/main.bicep | 168 ++++++++++ avm/ptn/azd/apim-api/main.json | 214 ++++++++++++ .../azd/apim-api/modules/apim-api-policy.xml | 92 ++++++ avm/ptn/azd/apim-api/modules/openapi.yaml | 312 ++++++++++++++++++ .../tests/e2e/defaults/dependencies.bicep | 84 +++++ .../tests/e2e/defaults/main.test.bicep | 63 ++++ avm/ptn/azd/apim-api/version.json | 7 + 11 files changed, 1247 insertions(+) create mode 100644 .github/workflows/avm.ptn.azd.apim-api.yml create mode 100644 avm/ptn/azd/apim-api/README.md create mode 100644 avm/ptn/azd/apim-api/main.bicep create mode 100644 avm/ptn/azd/apim-api/main.json create mode 100644 avm/ptn/azd/apim-api/modules/apim-api-policy.xml create mode 100644 avm/ptn/azd/apim-api/modules/openapi.yaml create mode 100644 avm/ptn/azd/apim-api/tests/e2e/defaults/dependencies.bicep create mode 100644 avm/ptn/azd/apim-api/tests/e2e/defaults/main.test.bicep create mode 100644 avm/ptn/azd/apim-api/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index c294eb5bc4..69fd75e5c7 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -12,6 +12,7 @@ #/avm/ptn/avd-lza/management-plane/ @Azure/avm-ptn-avd-lza-managementplane-module-owners-bicep @Azure/avm-module-reviewers-bicep #/avm/ptn/avd-lza/networking/ @Azure/avm-ptn-avd-lza-networking-module-owners-bicep @Azure/avm-module-reviewers-bicep #/avm/ptn/avd-lza/session-hosts/ @Azure/avm-ptn-avd-lza-sessionhosts-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/ptn/azd/apim-api/ @Azure/avm-ptn-azd-apimapi-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/azd/container-apps/ @Azure/avm-ptn-azd-containerapps-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/azd/insights-dashboard/ @Azure/avm-ptn-azd-insightsdashboard-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/deployment-script/import-image-to-acr/ @Azure/avm-ptn-deploymentscript-importimagetoacr-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index bc5bdfbed5..76f4173ed1 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -47,6 +47,7 @@ body: # - "avm/ptn/avd-lza/management-plane" # - "avm/ptn/avd-lza/networking" # - "avm/ptn/avd-lza/session-hosts" + - "avm/ptn/azd/apim-api" - "avm/ptn/azd/container-apps" - "avm/ptn/azd/insights-dashboard" - "avm/ptn/deployment-script/import-image-to-acr" diff --git a/.github/workflows/avm.ptn.azd.apim-api.yml b/.github/workflows/avm.ptn.azd.apim-api.yml new file mode 100644 index 0000000000..f02556626c --- /dev/null +++ b/.github/workflows/avm.ptn.azd.apim-api.yml @@ -0,0 +1,88 @@ +name: "avm.ptn.azd.apim-api" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.ptn.azd.apim-api.yml" + - "avm/ptn/azd/apim-api/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/ptn/azd/apim-api" + workflowPath: ".github/workflows/avm.ptn.azd.apim-api.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit \ No newline at end of file diff --git a/avm/ptn/azd/apim-api/README.md b/avm/ptn/azd/apim-api/README.md new file mode 100644 index 0000000000..681638af79 --- /dev/null +++ b/avm/ptn/azd/apim-api/README.md @@ -0,0 +1,217 @@ +# avm/ptn/azd/apim-api `[Azd/ApimApi]` + +Creates and configure an API within an API Management service instance. + +**Note:** This module is not intended for broad, generic use, as it was designed to cater for the requirements of the AZD CLI product. Feature requests and bug fix requests are welcome if they support the development of the AZD CLI but may not be incorporated if they aim to make this module more generic than what it needs to be for its primary use case. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ApiManagement/service/apis` | [2022-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2022-08-01/service/apis) | +| `Microsoft.ApiManagement/service/apis/diagnostics` | [2022-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2022-08-01/service/apis/diagnostics) | +| `Microsoft.ApiManagement/service/apis/policies` | [2022-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ApiManagement/2022-08-01/service/apis/policies) | +| `Microsoft.Web/sites/config` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/ptn/azd/apim-api:`. + +- [Using only defaults](#example-1-using-only-defaults) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module apimApi 'br/public:avm/ptn/azd/apim-api:' = { + name: 'apimApiDeployment' + params: { + // Required parameters + apiBackendUrl: '' + apiDescription: 'api description' + apiDisplayName: 'apd-aapmin' + apiName: 'an-aapmin001' + apiPath: 'apipath-aapmin' + name: '' + webFrontendUrl: '' + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "apiBackendUrl": { + "value": "" + }, + "apiDescription": { + "value": "api description" + }, + "apiDisplayName": { + "value": "apd-aapmin" + }, + "apiName": { + "value": "an-aapmin001" + }, + "apiPath": { + "value": "apipath-aapmin" + }, + "name": { + "value": "" + }, + "webFrontendUrl": { + "value": "" + }, + // Non-required parameters + "location": { + "value": "" + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`apiBackendUrl`](#parameter-apibackendurl) | string | Absolute URL of the backend service implementing this API. | +| [`apiDescription`](#parameter-apidescription) | string | Description of the API. May include HTML formatting tags. | +| [`apiDisplayName`](#parameter-apidisplayname) | string | The Display Name of the API. | +| [`apiName`](#parameter-apiname) | string | Resource name to uniquely identify this API within the API Management service instance. | +| [`apiPath`](#parameter-apipath) | string | Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API. | +| [`name`](#parameter-name) | string | Name of the API Management service instance. | +| [`webFrontendUrl`](#parameter-webfrontendurl) | string | Absolute URL of the web frontend. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`apiAppName`](#parameter-apiappname) | string | Resource name for backend Web App or Function App. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`location`](#parameter-location) | string | Location for all Resources. | + +### Parameter: `apiBackendUrl` + +Absolute URL of the backend service implementing this API. + +- Required: Yes +- Type: string + +### Parameter: `apiDescription` + +Description of the API. May include HTML formatting tags. + +- Required: Yes +- Type: string + +### Parameter: `apiDisplayName` + +The Display Name of the API. + +- Required: Yes +- Type: string + +### Parameter: `apiName` + +Resource name to uniquely identify this API within the API Management service instance. + +- Required: Yes +- Type: string + +### Parameter: `apiPath` + +Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API. + +- Required: Yes +- Type: string + +### Parameter: `name` + +Name of the API Management service instance. + +- Required: Yes +- Type: string + +### Parameter: `webFrontendUrl` + +Absolute URL of the web frontend. + +- Required: Yes +- Type: string + +### Parameter: `apiAppName` + +Resource name for backend Web App or Function App. + +- Required: No +- Type: string +- Default: `''` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `location` + +Location for all Resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `resourceGroupName` | string | The name of the resource group. | +| `serviceApiUri` | string | The complete URL for accessing the API. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/ptn/azd/apim-api/main.bicep b/avm/ptn/azd/apim-api/main.bicep new file mode 100644 index 0000000000..22c2fd5435 --- /dev/null +++ b/avm/ptn/azd/apim-api/main.bicep @@ -0,0 +1,168 @@ +metadata name = 'avm/ptn/azd/apim-api' +metadata description = '''Creates and configure an API within an API Management service instance. + +**Note:** This module is not intended for broad, generic use, as it was designed to cater for the requirements of the AZD CLI product. Feature requests and bug fix requests are welcome if they support the development of the AZD CLI but may not be incorporated if they aim to make this module more generic than what it needs to be for its primary use case.''' +metadata owner = 'Azure/module-maintainers' + +@description('Required. Name of the API Management service instance.') +param name string + +@description('Required. Resource name to uniquely identify this API within the API Management service instance.') +@minLength(1) +param apiName string + +@description('Required. The Display Name of the API.') +@minLength(1) +@maxLength(300) +param apiDisplayName string + +@description('Required. Description of the API. May include HTML formatting tags.') +@minLength(1) +param apiDescription string + +@description('Required. Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API.') +@minLength(1) +param apiPath string + +@description('Required. Absolute URL of the web frontend.') +param webFrontendUrl string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Required. Absolute URL of the backend service implementing this API.') +param apiBackendUrl string + +@description('Optional. Resource name for backend Web App or Function App.') +param apiAppName string = '' + +// ============== // +// Variables // +// ============== // + +var apiPolicyContent = replace(loadTextContent('modules/apim-api-policy.xml'), '{origin}', webFrontendUrl) + +// Necessary due to https://github.com/Azure/bicep/issues/9594 +// placeholderName is never deployed, it is merely used to make the child name validation pass +var appNameForBicep = !empty(apiAppName) ? apiAppName : 'placeholderName' + +// ============== // +// Resources // +// ============== // + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { + name: '46d3xbcp.ptn.azd-apimapi.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } +} + +resource restApi 'Microsoft.ApiManagement/service/apis@2022-08-01' = { + name: apiName + parent: apimService + properties: { + description: apiDescription + displayName: apiDisplayName + path: apiPath + protocols: ['https'] + subscriptionRequired: false + type: 'http' + format: 'openapi' + serviceUrl: apiBackendUrl + value: loadTextContent('modules/openapi.yaml') + } +} + +resource apiPolicy 'Microsoft.ApiManagement/service/apis/policies@2022-08-01' = { + name: 'policy' + parent: restApi + properties: { + format: 'rawxml' + value: apiPolicyContent + } +} + +resource apiDiagnostics 'Microsoft.ApiManagement/service/apis/diagnostics@2022-08-01' = { + name: 'applicationinsights' + parent: restApi + properties: { + alwaysLog: 'allErrors' + backend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + frontend: { + request: { + body: { + bytes: 1024 + } + } + response: { + body: { + bytes: 1024 + } + } + } + httpCorrelationProtocol: 'W3C' + logClientIp: true + loggerId: apimLogger.id + metrics: true + sampling: { + percentage: 100 + samplingType: 'fixed' + } + verbosity: 'verbose' + } +} + +resource apimService 'Microsoft.ApiManagement/service@2022-08-01' existing = { + name: name +} + +resource apiAppProperties 'Microsoft.Web/sites/config@2022-09-01' = if (!empty(apiAppName)) { + name: '${appNameForBicep}/web' + kind: 'string' + properties: { + apiManagementConfig: { + id: '${apimService.id}/apis/${apiName}' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2022-08-01' existing = { + name: 'app-insights-logger' + parent: apimService +} + +// ============ // +// Outputs // +// ============ // + +@description('The name of the resource group.') +output resourceGroupName string = resourceGroup().name + +@description('The complete URL for accessing the API.') +output serviceApiUri string = '${apimService.properties.gatewayUrl}/${apiPath}' diff --git a/avm/ptn/azd/apim-api/main.json b/avm/ptn/azd/apim-api/main.json new file mode 100644 index 0000000000..dacd440b8a --- /dev/null +++ b/avm/ptn/azd/apim-api/main.json @@ -0,0 +1,214 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "7935494033060946539" + }, + "name": "avm/ptn/azd/apim-api", + "description": "Creates and configure an API within an API Management service instance.\n\n**Note:** This module is not intended for broad, generic use, as it was designed to cater for the requirements of the AZD CLI product. Feature requests and bug fix requests are welcome if they support the development of the AZD CLI but may not be incorporated if they aim to make this module more generic than what it needs to be for its primary use case.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the API Management service instance." + } + }, + "apiName": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Required. Resource name to uniquely identify this API within the API Management service instance." + } + }, + "apiDisplayName": { + "type": "string", + "minLength": 1, + "maxLength": 300, + "metadata": { + "description": "Required. The Display Name of the API." + } + }, + "apiDescription": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Required. Description of the API. May include HTML formatting tags." + } + }, + "apiPath": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Required. Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API." + } + }, + "webFrontendUrl": { + "type": "string", + "metadata": { + "description": "Required. Absolute URL of the web frontend." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "apiBackendUrl": { + "type": "string", + "metadata": { + "description": "Required. Absolute URL of the backend service implementing this API." + } + }, + "apiAppName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource name for backend Web App or Function App." + } + } + }, + "variables": { + "$fxv#0": "\n\n \n \n \n \n \n {origin}\n \n \n PUT\n GET\n POST\n DELETE\n PATCH\n \n \n

*
\n \n \n
*
\n
\n \n \n \n \n \n \n \n Call to the @(context.Api.Name)\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n = 200 && context.Response.StatusCode < 300)\">\n \n \n \n \n \n \n \n = 400 && context.Response.StatusCode < 600)\">\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n Failed to process the @(context.Api.Name)\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n An unexpected error has occurred.\n \n \n", + "$fxv#1": "openapi: 3.0.0\ninfo:\n description: Simple Todo API\n version: 3.0.0\n title: Simple Todo API\n contact:\n email: azdevteam@microsoft.com\n\ncomponents:\n schemas:\n TodoItem:\n type: object\n required:\n - listId\n - name\n - description\n description: A task that needs to be completed\n properties:\n id:\n type: string\n listId:\n type: string\n name:\n type: string\n description:\n type: string\n state:\n $ref: \"#/components/schemas/TodoState\"\n dueDate:\n type: string\n format: date-time\n completedDate:\n type: string\n format: date-time\n TodoList:\n type: object\n required:\n - name\n properties:\n id:\n type: string\n name:\n type: string\n description:\n type: string\n description: \" A list of related Todo items\"\n TodoState:\n type: string\n enum:\n - todo\n - inprogress\n - done\n parameters:\n listId:\n in: path\n required: true\n name: listId\n description: The Todo list unique identifier\n schema:\n type: string\n itemId:\n in: path\n required: true\n name: itemId\n description: The Todo item unique identifier\n schema:\n type: string\n state:\n in: path\n required: true\n name: state\n description: The Todo item state\n schema:\n $ref: \"#/components/schemas/TodoState\"\n top:\n in: query\n required: false\n name: top\n description: The max number of items to returns in a result\n schema:\n type: number\n default: 20\n skip:\n in: query\n required: false\n name: skip\n description: The number of items to skip within the results\n schema:\n type: number\n default: 0\n\n requestBodies:\n TodoList:\n description: The Todo List\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoList\"\n TodoItem:\n description: The Todo Item\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoItem\"\n\n responses:\n TodoList:\n description: A Todo list result\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoList\"\n TodoListArray:\n description: An array of Todo lists\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: \"#/components/schemas/TodoList\"\n TodoItem:\n description: A Todo item result\n content:\n application/json:\n schema:\n $ref: \"#/components/schemas/TodoItem\"\n TodoItemArray:\n description: An array of Todo items\n content:\n application/json:\n schema:\n type: array\n items:\n $ref: \"#/components/schemas/TodoItem\"\n\npaths:\n /lists:\n get:\n operationId: GetLists\n summary: Gets an array of Todo lists\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoListArray\"\n post:\n operationId: CreateList\n summary: Creates a new Todo list\n tags:\n - Lists\n requestBody:\n $ref: \"#/components/requestBodies/TodoList\"\n responses:\n 201:\n $ref: \"#/components/responses/TodoList\"\n 400:\n description: Invalid request schema\n /lists/{listId}:\n get:\n operationId: GetListById\n summary: Gets a Todo list by unique identifier\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoList\"\n 404:\n description: Todo list not found\n put:\n operationId: UpdateListById\n summary: Updates a Todo list by unique identifier\n tags:\n - Lists\n requestBody:\n $ref: \"#/components/requestBodies/TodoList\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoList\"\n 404:\n description: Todo list not found\n 400:\n description: Todo list is invalid\n delete:\n operationId: DeleteListById\n summary: Deletes a Todo list by unique identifier\n tags:\n - Lists\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 204:\n description: Todo list deleted successfully\n 404:\n description: Todo list not found\n /lists/{listId}/items:\n post:\n operationId: CreateItem\n summary: Creates a new Todo item within a list\n tags:\n - Items\n requestBody:\n $ref: \"#/components/requestBodies/TodoItem\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n responses:\n 201:\n $ref: \"#/components/responses/TodoItem\"\n 404:\n description: Todo list not found\n get:\n operationId: GetItemsByListId\n summary: Gets Todo items within the specified list\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItemArray\"\n 404:\n description: Todo list not found\n /lists/{listId}/items/{itemId}:\n get:\n operationId: GetItemById\n summary: Gets a Todo item by unique identifier\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItem\"\n 404:\n description: Todo list or item not found\n put:\n operationId: UpdateItemById\n summary: Updates a Todo item by unique identifier\n tags:\n - Items\n requestBody:\n $ref: \"#/components/requestBodies/TodoItem\"\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItem\"\n 400:\n description: Todo item is invalid\n 404:\n description: Todo list or item not found\n delete:\n operationId: DeleteItemById\n summary: Deletes a Todo item by unique identifier\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/itemId\"\n responses:\n 204:\n description: Todo item deleted successfully\n 404:\n description: Todo list or item not found\n /lists/{listId}/items/state/{state}:\n get:\n operationId: GetItemsByListIdAndState\n summary: Gets a list of Todo items of a specific state\n tags:\n - Items\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/state\"\n - $ref: \"#/components/parameters/top\"\n - $ref: \"#/components/parameters/skip\"\n responses:\n 200:\n $ref: \"#/components/responses/TodoItemArray\"\n 404:\n description: Todo list or item not found\n put:\n operationId: UpdateItemsStateByListId\n summary: Changes the state of the specified list items\n tags:\n - Items\n requestBody:\n description: unique identifiers of the Todo items to update\n content:\n application/json:\n schema:\n type: array\n items:\n description: The Todo item unique identifier\n type: string\n parameters:\n - $ref: \"#/components/parameters/listId\"\n - $ref: \"#/components/parameters/state\"\n responses:\n 204:\n description: Todo items updated\n 400:\n description: Update request is invalid", + "apiPolicyContent": "[replace(variables('$fxv#0'), '{origin}', parameters('webFrontendUrl'))]", + "appNameForBicep": "[if(not(empty(parameters('apiAppName'))), parameters('apiAppName'), 'placeholderName')]" + }, + "resources": [ + { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.ptn.azd-apimapi.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + { + "type": "Microsoft.ApiManagement/service/apis", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}', parameters('name'), parameters('apiName'))]", + "properties": { + "description": "[parameters('apiDescription')]", + "displayName": "[parameters('apiDisplayName')]", + "path": "[parameters('apiPath')]", + "protocols": [ + "https" + ], + "subscriptionRequired": false, + "type": "http", + "format": "openapi", + "serviceUrl": "[parameters('apiBackendUrl')]", + "value": "[variables('$fxv#1')]" + } + }, + { + "type": "Microsoft.ApiManagement/service/apis/policies", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}/{2}', parameters('name'), parameters('apiName'), 'policy')]", + "properties": { + "format": "rawxml", + "value": "[variables('apiPolicyContent')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', parameters('name'), parameters('apiName'))]" + ] + }, + { + "type": "Microsoft.ApiManagement/service/apis/diagnostics", + "apiVersion": "2022-08-01", + "name": "[format('{0}/{1}/{2}', parameters('name'), parameters('apiName'), 'applicationinsights')]", + "properties": { + "alwaysLog": "allErrors", + "backend": { + "request": { + "body": { + "bytes": 1024 + } + }, + "response": { + "body": { + "bytes": 1024 + } + } + }, + "frontend": { + "request": { + "body": { + "bytes": 1024 + } + }, + "response": { + "body": { + "bytes": 1024 + } + } + }, + "httpCorrelationProtocol": "W3C", + "logClientIp": true, + "loggerId": "[resourceId('Microsoft.ApiManagement/service/loggers', parameters('name'), 'app-insights-logger')]", + "metrics": true, + "sampling": { + "percentage": 100, + "samplingType": "fixed" + }, + "verbosity": "verbose" + }, + "dependsOn": [ + "[resourceId('Microsoft.ApiManagement/service/apis', parameters('name'), parameters('apiName'))]" + ] + }, + { + "condition": "[not(empty(parameters('apiAppName')))]", + "type": "Microsoft.Web/sites/config", + "apiVersion": "2022-09-01", + "name": "[format('{0}/web', variables('appNameForBicep'))]", + "kind": "string", + "properties": { + "apiManagementConfig": { + "id": "[format('{0}/apis/{1}', resourceId('Microsoft.ApiManagement/service', parameters('name')), parameters('apiName'))]" + } + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group." + }, + "value": "[resourceGroup().name]" + }, + "serviceApiUri": { + "type": "string", + "metadata": { + "description": "The complete URL for accessing the API." + }, + "value": "[format('{0}/{1}', reference(resourceId('Microsoft.ApiManagement/service', parameters('name')), '2022-08-01').gatewayUrl, parameters('apiPath'))]" + } + } +} \ No newline at end of file diff --git a/avm/ptn/azd/apim-api/modules/apim-api-policy.xml b/avm/ptn/azd/apim-api/modules/apim-api-policy.xml new file mode 100644 index 0000000000..3c42f53fce --- /dev/null +++ b/avm/ptn/azd/apim-api/modules/apim-api-policy.xml @@ -0,0 +1,92 @@ + + + + + + + + {origin} + + + PUT + GET + POST + DELETE + PATCH + + +
*
+
+ +
*
+
+
+ + + + + + + Call to the @(context.Api.Name) + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Failed to process the @(context.Api.Name) + + + + + + + + + + + + + + + + + + An unexpected error has occurred. + + +
\ No newline at end of file diff --git a/avm/ptn/azd/apim-api/modules/openapi.yaml b/avm/ptn/azd/apim-api/modules/openapi.yaml new file mode 100644 index 0000000000..e28b058860 --- /dev/null +++ b/avm/ptn/azd/apim-api/modules/openapi.yaml @@ -0,0 +1,312 @@ +openapi: 3.0.0 +info: + description: Simple Todo API + version: 3.0.0 + title: Simple Todo API + contact: + email: azdevteam@microsoft.com + +components: + schemas: + TodoItem: + type: object + required: + - listId + - name + - description + description: A task that needs to be completed + properties: + id: + type: string + listId: + type: string + name: + type: string + description: + type: string + state: + $ref: "#/components/schemas/TodoState" + dueDate: + type: string + format: date-time + completedDate: + type: string + format: date-time + TodoList: + type: object + required: + - name + properties: + id: + type: string + name: + type: string + description: + type: string + description: " A list of related Todo items" + TodoState: + type: string + enum: + - todo + - inprogress + - done + parameters: + listId: + in: path + required: true + name: listId + description: The Todo list unique identifier + schema: + type: string + itemId: + in: path + required: true + name: itemId + description: The Todo item unique identifier + schema: + type: string + state: + in: path + required: true + name: state + description: The Todo item state + schema: + $ref: "#/components/schemas/TodoState" + top: + in: query + required: false + name: top + description: The max number of items to returns in a result + schema: + type: number + default: 20 + skip: + in: query + required: false + name: skip + description: The number of items to skip within the results + schema: + type: number + default: 0 + + requestBodies: + TodoList: + description: The Todo List + content: + application/json: + schema: + $ref: "#/components/schemas/TodoList" + TodoItem: + description: The Todo Item + content: + application/json: + schema: + $ref: "#/components/schemas/TodoItem" + + responses: + TodoList: + description: A Todo list result + content: + application/json: + schema: + $ref: "#/components/schemas/TodoList" + TodoListArray: + description: An array of Todo lists + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TodoList" + TodoItem: + description: A Todo item result + content: + application/json: + schema: + $ref: "#/components/schemas/TodoItem" + TodoItemArray: + description: An array of Todo items + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/TodoItem" + +paths: + /lists: + get: + operationId: GetLists + summary: Gets an array of Todo lists + tags: + - Lists + parameters: + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoListArray" + post: + operationId: CreateList + summary: Creates a new Todo list + tags: + - Lists + requestBody: + $ref: "#/components/requestBodies/TodoList" + responses: + 201: + $ref: "#/components/responses/TodoList" + 400: + description: Invalid request schema + /lists/{listId}: + get: + operationId: GetListById + summary: Gets a Todo list by unique identifier + tags: + - Lists + parameters: + - $ref: "#/components/parameters/listId" + responses: + 200: + $ref: "#/components/responses/TodoList" + 404: + description: Todo list not found + put: + operationId: UpdateListById + summary: Updates a Todo list by unique identifier + tags: + - Lists + requestBody: + $ref: "#/components/requestBodies/TodoList" + parameters: + - $ref: "#/components/parameters/listId" + responses: + 200: + $ref: "#/components/responses/TodoList" + 404: + description: Todo list not found + 400: + description: Todo list is invalid + delete: + operationId: DeleteListById + summary: Deletes a Todo list by unique identifier + tags: + - Lists + parameters: + - $ref: "#/components/parameters/listId" + responses: + 204: + description: Todo list deleted successfully + 404: + description: Todo list not found + /lists/{listId}/items: + post: + operationId: CreateItem + summary: Creates a new Todo item within a list + tags: + - Items + requestBody: + $ref: "#/components/requestBodies/TodoItem" + parameters: + - $ref: "#/components/parameters/listId" + responses: + 201: + $ref: "#/components/responses/TodoItem" + 404: + description: Todo list not found + get: + operationId: GetItemsByListId + summary: Gets Todo items within the specified list + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoItemArray" + 404: + description: Todo list not found + /lists/{listId}/items/{itemId}: + get: + operationId: GetItemById + summary: Gets a Todo item by unique identifier + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 200: + $ref: "#/components/responses/TodoItem" + 404: + description: Todo list or item not found + put: + operationId: UpdateItemById + summary: Updates a Todo item by unique identifier + tags: + - Items + requestBody: + $ref: "#/components/requestBodies/TodoItem" + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 200: + $ref: "#/components/responses/TodoItem" + 400: + description: Todo item is invalid + 404: + description: Todo list or item not found + delete: + operationId: DeleteItemById + summary: Deletes a Todo item by unique identifier + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/itemId" + responses: + 204: + description: Todo item deleted successfully + 404: + description: Todo list or item not found + /lists/{listId}/items/state/{state}: + get: + operationId: GetItemsByListIdAndState + summary: Gets a list of Todo items of a specific state + tags: + - Items + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/state" + - $ref: "#/components/parameters/top" + - $ref: "#/components/parameters/skip" + responses: + 200: + $ref: "#/components/responses/TodoItemArray" + 404: + description: Todo list or item not found + put: + operationId: UpdateItemsStateByListId + summary: Changes the state of the specified list items + tags: + - Items + requestBody: + description: unique identifiers of the Todo items to update + content: + application/json: + schema: + type: array + items: + description: The Todo item unique identifier + type: string + parameters: + - $ref: "#/components/parameters/listId" + - $ref: "#/components/parameters/state" + responses: + 204: + description: Todo items updated + 400: + description: Update request is invalid \ No newline at end of file diff --git a/avm/ptn/azd/apim-api/tests/e2e/defaults/dependencies.bicep b/avm/ptn/azd/apim-api/tests/e2e/defaults/dependencies.bicep new file mode 100644 index 0000000000..fd100a6b64 --- /dev/null +++ b/avm/ptn/azd/apim-api/tests/e2e/defaults/dependencies.bicep @@ -0,0 +1,84 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the API Management service to create.') +param apimServiceName string + +@description('Required. The name of the owner of the API Management service.') +param publisherName string + +@description('Required. The name of the App Service to create.') +param appServiceName string + +@description('Required. The name of the App Service Plan to create.') +param appServicePlanName string + +@description('Required. The name of the Log Analytics Workspace to create.') +param logAnalyticsWorkspaceName string + +@description('Required. The name of the Application Insights to create.') +param applicationInsightsName string + +module apimService 'br/public:avm/res/api-management/service:0.4.0' = { + name: 'serviceDeployment' + params: { + name: apimServiceName + publisherEmail: 'apimgmt-noreply@mail.windowsazure.com' + publisherName: publisherName + location: location + loggers: [ + { + loggerType: 'applicationInsights' + name: 'app-insights-logger' + credentials: { + instrumentationKey: applicationInsights.properties.InstrumentationKey + } + resourceId: applicationInsights.id + } + ] + } +} + +module app 'br/public:avm/res/web/site:0.6.0' = { + name: 'siteDeployment' + params: { + kind: 'app' + name: appServiceName + serverFarmResourceId: appServicePlan.outputs.resourceId + } +} + +module appServicePlan 'br/public:avm/res/web/serverfarm:0.2.2' = { + name: 'serverDeployment' + params: { + name: appServicePlanName + skuCapacity: 2 + skuName: 'S1' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { + name: logAnalyticsWorkspaceName + location: location + properties: { + sku: { + name: 'PerGB2018' + } + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: applicationInsightsName + location: location + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspace.id + } +} + +@description('The default hostname of the site.') +output siteHostName string = 'https://${app.outputs.defaultHostname}' + +@description('The name of the API Management service.') +output apimName string = apimService.outputs.name diff --git a/avm/ptn/azd/apim-api/tests/e2e/defaults/main.test.bicep b/avm/ptn/azd/apim-api/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..27f5f5a9dc --- /dev/null +++ b/avm/ptn/azd/apim-api/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,63 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-apim-api-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'aapmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + appServicePlanName: 'dep-${namePrefix}-sp-${serviceShort}' + appServiceName: 'dep-${namePrefix}-aps-${serviceShort}' + apimServiceName: '${namePrefix}-as-${serviceShort}001' + publisherName: 'dep-${namePrefix}-pn-x-001' + applicationInsightsName: 'dep-${namePrefix}-ais-${serviceShort}' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + location: resourceLocation + name: nestedDependencies.outputs.apimName + apiDisplayName: '${namePrefix}-apd-${serviceShort}' + apiPath: '${namePrefix}-apipath-${serviceShort}' + webFrontendUrl: nestedDependencies.outputs.siteHostName + apiBackendUrl: nestedDependencies.outputs.siteHostName + apiDescription: 'api description' + apiName: '${namePrefix}-an-${serviceShort}001' + } +} diff --git a/avm/ptn/azd/apim-api/version.json b/avm/ptn/azd/apim-api/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/ptn/azd/apim-api/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From 97f616fd68c1366d2d062052edec35dc12defd67 Mon Sep 17 00:00:00 2001 From: Buddy <38195643+tsc-buddy@users.noreply.github.com> Date: Wed, 11 Sep 2024 23:09:43 +1200 Subject: [PATCH 13/57] feat: Service Bus Namespace AZ Resiliency Updates (#3248) ## Description The changes in this PR address the requirement for Zone Redundant configurations by default. ## Pipeline Reference | Pipeline | | -------- | |[![avm.res.service-bus.namespace](https://github.com/tsc-buddy/bicep-registry-modules/actions/workflows/avm.res.service-bus.namespace.yml/badge.svg?branch=feat%2Fsb-az-defaults)](https://github.com/tsc-buddy/bicep-registry-modules/actions/workflows/avm.res.service-bus.namespace.yml)| ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [x] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/service-bus/namespace/README.md | 105 +++++++++--------- avm/res/service-bus/namespace/main.bicep | 11 +- avm/res/service-bus/namespace/main.json | 12 +- .../tests/e2e/defaults/main.test.bicep | 3 +- .../namespace/tests/e2e/max/main.test.bicep | 1 - .../tests/e2e/waf-aligned/main.test.bicep | 1 - avm/res/service-bus/namespace/version.json | 2 +- 7 files changed, 71 insertions(+), 64 deletions(-) diff --git a/avm/res/service-bus/namespace/README.md b/avm/res/service-bus/namespace/README.md index 6cee870109..0e05ec12f1 100644 --- a/avm/res/service-bus/namespace/README.md +++ b/avm/res/service-bus/namespace/README.md @@ -59,11 +59,12 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { params: { // Required parameters name: 'sbnmin001' - skuObject: { - name: 'Basic' - } // Non-required parameters location: '' + skuObject: { + capacity: 2 + name: 'Premium' + } } } ``` @@ -84,14 +85,15 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { "name": { "value": "sbnmin001" }, - "skuObject": { - "value": { - "name": "Basic" - } - }, // Non-required parameters "location": { "value": "" + }, + "skuObject": { + "value": { + "capacity": 2, + "name": "Premium" + } } } } @@ -115,10 +117,6 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { params: { // Required parameters name: 'sbnencr001' - skuObject: { - capacity: 1 - name: 'Premium' - } // Non-required parameters customerManagedKey: { keyName: '' @@ -132,6 +130,10 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { '' ] } + skuObject: { + capacity: 1 + name: 'Premium' + } } } ``` @@ -152,12 +154,6 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { "name": { "value": "sbnencr001" }, - "skuObject": { - "value": { - "capacity": 1, - "name": "Premium" - } - }, // Non-required parameters "customerManagedKey": { "value": { @@ -176,6 +172,12 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { "" ] } + }, + "skuObject": { + "value": { + "capacity": 1, + "name": "Premium" + } } } } @@ -199,10 +201,6 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { params: { // Required parameters name: 'sbnmax001' - skuObject: { - capacity: 16 - name: 'Premium' - } // Non-required parameters authorizationRules: [ { @@ -382,6 +380,10 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { roleDefinitionIdOrName: '' } ] + skuObject: { + capacity: 16 + name: 'Premium' + } tags: { Environment: 'Non-Prod' 'hidden-title': 'This is visible in the resource name' @@ -431,7 +433,6 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { ] } ] - zoneRedundant: true } } ``` @@ -452,12 +453,6 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { "name": { "value": "sbnmax001" }, - "skuObject": { - "value": { - "capacity": 16, - "name": "Premium" - } - }, // Non-required parameters "authorizationRules": { "value": [ @@ -663,6 +658,12 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { } ] }, + "skuObject": { + "value": { + "capacity": 16, + "name": "Premium" + } + }, "tags": { "value": { "Environment": "Non-Prod", @@ -715,9 +716,6 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { ] } ] - }, - "zoneRedundant": { - "value": true } } } @@ -741,10 +739,6 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { params: { // Required parameters name: 'sbnwaf001' - skuObject: { - capacity: 2 - name: 'Premium' - } // Non-required parameters authorizationRules: [ { @@ -850,6 +844,10 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { } ] roleAssignments: [] + skuObject: { + capacity: 2 + name: 'Premium' + } tags: { Environment: 'Non-Prod' 'hidden-title': 'This is visible in the resource name' @@ -878,7 +876,6 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { roleAssignments: [] } ] - zoneRedundant: true } } ``` @@ -899,12 +896,6 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { "name": { "value": "sbnwaf001" }, - "skuObject": { - "value": { - "capacity": 2, - "name": "Premium" - } - }, // Non-required parameters "authorizationRules": { "value": [ @@ -1036,6 +1027,12 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { "roleAssignments": { "value": [] }, + "skuObject": { + "value": { + "capacity": 2, + "name": "Premium" + } + }, "tags": { "value": { "Environment": "Non-Prod", @@ -1067,9 +1064,6 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { "roleAssignments": [] } ] - }, - "zoneRedundant": { - "value": true } } } @@ -1085,7 +1079,7 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { | Parameter | Type | Description | | :-- | :-- | :-- | | [`name`](#parameter-name) | string | Name of the Service Bus Namespace. | -| [`skuObject`](#parameter-skuobject) | object | The SKU of the Service Bus Namespace. | +| [`skuObject`](#parameter-skuobject) | object | The SKU of the Service Bus Namespace. Defaulted to Premium for ZoneRedundant configurations by default. | **Optional parameters** @@ -1112,7 +1106,7 @@ module namespace 'br/public:avm/res/service-bus/namespace:' = { | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`tags`](#parameter-tags) | object | Tags of the resource. | | [`topics`](#parameter-topics) | array | The topics to create in the service bus namespace. | -| [`zoneRedundant`](#parameter-zoneredundant) | bool | Enabling this property creates a Premium Service Bus Namespace in regions supported availability zones. | +| [`zoneRedundant`](#parameter-zoneredundant) | bool | Enabled by default in order to align with resiliency best practices, thus requires Premium SKU. | ### Parameter: `name` @@ -1123,10 +1117,17 @@ Name of the Service Bus Namespace. ### Parameter: `skuObject` -The SKU of the Service Bus Namespace. +The SKU of the Service Bus Namespace. Defaulted to Premium for ZoneRedundant configurations by default. -- Required: Yes +- Required: No - Type: object +- Default: + ```Bicep + { + capacity: 2 + name: 'Premium' + } + ``` **Required parameters** @@ -3069,11 +3070,11 @@ Value that indicates whether the topic supports ordering. ### Parameter: `zoneRedundant` -Enabling this property creates a Premium Service Bus Namespace in regions supported availability zones. +Enabled by default in order to align with resiliency best practices, thus requires Premium SKU. - Required: No - Type: bool -- Default: `False` +- Default: `True` ## Outputs diff --git a/avm/res/service-bus/namespace/main.bicep b/avm/res/service-bus/namespace/main.bicep index 08ecf97e72..7b00c06d4c 100644 --- a/avm/res/service-bus/namespace/main.bicep +++ b/avm/res/service-bus/namespace/main.bicep @@ -9,11 +9,14 @@ param name string @description('Optional. Location for all resources.') param location string = resourceGroup().location -@description('Required. The SKU of the Service Bus Namespace.') -param skuObject skuType +@description('Required. The SKU of the Service Bus Namespace. Defaulted to Premium for ZoneRedundant configurations by default.') +param skuObject skuType = { + name: 'Premium' + capacity: 2 +} -@description('Optional. Enabling this property creates a Premium Service Bus Namespace in regions supported availability zones.') -param zoneRedundant bool = false +@description('Optional. Enabled by default in order to align with resiliency best practices, thus requires Premium SKU.') +param zoneRedundant bool = true @allowed([ '1.0' diff --git a/avm/res/service-bus/namespace/main.json b/avm/res/service-bus/namespace/main.json index b3917916f0..8b30c416be 100644 --- a/avm/res/service-bus/namespace/main.json +++ b/avm/res/service-bus/namespace/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "16628890374295506516" + "templateHash": "6397771352503979306" }, "name": "Service Bus Namespaces", "description": "This module deploys a Service Bus Namespace.", @@ -1137,15 +1137,19 @@ }, "skuObject": { "$ref": "#/definitions/skuType", + "defaultValue": { + "name": "Premium", + "capacity": 2 + }, "metadata": { - "description": "Required. The SKU of the Service Bus Namespace." + "description": "Required. The SKU of the Service Bus Namespace. Defaulted to Premium for ZoneRedundant configurations by default." } }, "zoneRedundant": { "type": "bool", - "defaultValue": false, + "defaultValue": true, "metadata": { - "description": "Optional. Enabling this property creates a Premium Service Bus Namespace in regions supported availability zones." + "description": "Optional. Enabled by default in order to align with resiliency best practices, thus requires Premium SKU." } }, "minimumTlsVersion": { diff --git a/avm/res/service-bus/namespace/tests/e2e/defaults/main.test.bicep b/avm/res/service-bus/namespace/tests/e2e/defaults/main.test.bicep index 3d75b0c635..067d0c6f69 100644 --- a/avm/res/service-bus/namespace/tests/e2e/defaults/main.test.bicep +++ b/avm/res/service-bus/namespace/tests/e2e/defaults/main.test.bicep @@ -44,7 +44,8 @@ module testDeployment '../../../main.bicep' = [ name: '${namePrefix}${serviceShort}001' location: resourceLocation skuObject: { - name: 'Basic' + name: 'Premium' + capacity: 2 } } } diff --git a/avm/res/service-bus/namespace/tests/e2e/max/main.test.bicep b/avm/res/service-bus/namespace/tests/e2e/max/main.test.bicep index 75f7aac495..fcf8d82aee 100644 --- a/avm/res/service-bus/namespace/tests/e2e/max/main.test.bicep +++ b/avm/res/service-bus/namespace/tests/e2e/max/main.test.bicep @@ -76,7 +76,6 @@ module testDeployment '../../../main.bicep' = [ capacity: 16 } premiumMessagingPartitions: 1 - zoneRedundant: true tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' diff --git a/avm/res/service-bus/namespace/tests/e2e/waf-aligned/main.test.bicep b/avm/res/service-bus/namespace/tests/e2e/waf-aligned/main.test.bicep index f40c29ea50..2160534080 100644 --- a/avm/res/service-bus/namespace/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/service-bus/namespace/tests/e2e/waf-aligned/main.test.bicep @@ -76,7 +76,6 @@ module testDeployment '../../../main.bicep' = [ capacity: 2 } premiumMessagingPartitions: 1 - zoneRedundant: true tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' diff --git a/avm/res/service-bus/namespace/version.json b/avm/res/service-bus/namespace/version.json index 9a9a06e897..6b6be93891 100644 --- a/avm/res/service-bus/namespace/version.json +++ b/avm/res/service-bus/namespace/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.8", + "version": "0.9", "pathFilters": [ "./main.json" ] From 4d7c891a3323450baed005c4893c1e75772502c6 Mon Sep 17 00:00:00 2001 From: Seif Bassem <38246040+sebassem@users.noreply.github.com> Date: Wed, 11 Sep 2024 21:25:36 +0300 Subject: [PATCH 14/57] fix: [avm-ptn-lz-sub-vending] Add improvements to the vwan connections naming convention (#3110) ## Description Fixes #3085 ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.lz.sub-vending](https://github.com/sebassem/bicep-registry-modules/actions/workflows/avm.ptn.lz.sub-vending.yml/badge.svg?branch=avm-ptn-lz-sub-vending-vwan-connection-name)](https://github.com/sebassem/bicep-registry-modules/actions/workflows/avm.ptn.lz.sub-vending.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [X] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/ptn/lz/sub-vending/main.json | 70 +++++++++---------- .../modules/readTagsResourceGroup.bicep | 2 +- .../modules/readTagsSubscription.bicep | 2 +- .../modules/subResourceWrapper.bicep | 2 +- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/avm/ptn/lz/sub-vending/main.json b/avm/ptn/lz/sub-vending/main.json index 7d14c11f82..35687dd6ea 100644 --- a/avm/ptn/lz/sub-vending/main.json +++ b/avm/ptn/lz/sub-vending/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "11010234208010675398" + "version": "0.29.47.4906", + "templateHash": "2409780926109914899" }, "name": "Sub-vending", "description": "This module deploys a subscription to accelerate deployment of landing zones. For more information on how to use it, please visit this [Wiki](https://github.com/Azure/bicep-lz-vending/wiki).", @@ -445,8 +445,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "3306118610933947345" + "version": "0.29.47.4906", + "templateHash": "3759867594724381121" } }, "parameters": { @@ -652,8 +652,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15097801658394168144" + "version": "0.29.47.4906", + "templateHash": "10394721304346895394" }, "name": "`/subResourcesWrapper/deploy.bicep` Parameters", "description": "This module is used by the [`bicep-lz-vending`](https://aka.ms/sub-vending/bicep) module to help orchestrate the deployment", @@ -992,7 +992,7 @@ "virtualWanHubName": "[if(not(empty(variables('virtualHubResourceIdChecked'))), split(variables('virtualHubResourceIdChecked'), '/')[8], '')]", "virtualWanHubSubscriptionId": "[if(not(empty(variables('virtualHubResourceIdChecked'))), split(variables('virtualHubResourceIdChecked'), '/')[2], '')]", "virtualWanHubResourceGroupName": "[if(not(empty(variables('virtualHubResourceIdChecked'))), split(variables('virtualHubResourceIdChecked'), '/')[4], '')]", - "virtualWanHubConnectionName": "[format('vhc-{0}', guid(variables('virtualHubResourceIdChecked'), parameters('virtualNetworkName'), parameters('virtualNetworkResourceGroupName'), parameters('virtualNetworkLocation'), parameters('subscriptionId')))]", + "virtualWanHubConnectionName": "[format('vhc-{0}-{1}', parameters('virtualNetworkName'), substring(guid(variables('virtualHubResourceIdChecked'), parameters('virtualNetworkName'), parameters('virtualNetworkResourceGroupName'), parameters('virtualNetworkLocation'), parameters('subscriptionId')), 0, 5))]", "virtualWanHubConnectionAssociatedRouteTable": "[if(not(empty(parameters('virtualNetworkVwanAssociatedRouteTableResourceId'))), parameters('virtualNetworkVwanAssociatedRouteTableResourceId'), format('{0}/hubRouteTables/defaultRouteTable', variables('virtualHubResourceIdChecked')))]", "virutalWanHubDefaultRouteTableId": { "id": "[format('{0}/hubRouteTables/defaultRouteTable', variables('virtualHubResourceIdChecked'))]" @@ -1028,8 +1028,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9313475896614087193" + "version": "0.29.47.4906", + "templateHash": "13961984943235030681" } }, "parameters": { @@ -1086,8 +1086,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15181721731905574940" + "version": "0.29.47.4906", + "templateHash": "6592820624705341105" } }, "parameters": { @@ -1146,8 +1146,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2065450289597496523" + "version": "0.29.47.4906", + "templateHash": "3589833223987550845" } }, "parameters": { @@ -1202,8 +1202,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4876221897054252048" + "version": "0.29.47.4906", + "templateHash": "15687156082548283745" } }, "parameters": { @@ -1222,7 +1222,7 @@ "metadata": { "description": "Tags currently applied to the subscription level" }, - "value": "[if(contains(reference(subscriptionResourceId('Microsoft.Resources/tags', parameters('name')), '2019-10-01'), 'tags'), reference(subscriptionResourceId('Microsoft.Resources/tags', parameters('name')), '2019-10-01').tags, createObject())]" + "value": "[coalesce(tryGet(reference(subscriptionResourceId('Microsoft.Resources/tags', parameters('name')), '2019-10-01'), 'tags'), createObject())]" } } } @@ -1280,8 +1280,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10772346649778391302" + "version": "0.29.47.4906", + "templateHash": "2885361202003966670" } }, "parameters": { @@ -1335,8 +1335,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "943757300004366392" + "version": "0.29.47.4906", + "templateHash": "4327209924100539632" } }, "parameters": { @@ -1355,7 +1355,7 @@ "metadata": { "description": "Tags currently applied to the subscription level" }, - "value": "[if(contains(reference(resourceId('Microsoft.Resources/tags', parameters('name')), '2019-10-01'), 'tags'), reference(resourceId('Microsoft.Resources/tags', parameters('name')), '2019-10-01').tags, createObject())]" + "value": "[coalesce(tryGet(reference(resourceId('Microsoft.Resources/tags', parameters('name')), '2019-10-01'), 'tags'), createObject())]" } } } @@ -1915,8 +1915,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15181721731905574940" + "version": "0.29.47.4906", + "templateHash": "6592820624705341105" } }, "parameters": { @@ -1975,8 +1975,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "2065450289597496523" + "version": "0.29.47.4906", + "templateHash": "3589833223987550845" } }, "parameters": { @@ -2031,8 +2031,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "4876221897054252048" + "version": "0.29.47.4906", + "templateHash": "15687156082548283745" } }, "parameters": { @@ -2051,7 +2051,7 @@ "metadata": { "description": "Tags currently applied to the subscription level" }, - "value": "[if(contains(reference(subscriptionResourceId('Microsoft.Resources/tags', parameters('name')), '2019-10-01'), 'tags'), reference(subscriptionResourceId('Microsoft.Resources/tags', parameters('name')), '2019-10-01').tags, createObject())]" + "value": "[coalesce(tryGet(reference(subscriptionResourceId('Microsoft.Resources/tags', parameters('name')), '2019-10-01'), 'tags'), createObject())]" } } } @@ -2109,8 +2109,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "10772346649778391302" + "version": "0.29.47.4906", + "templateHash": "2885361202003966670" } }, "parameters": { @@ -2164,8 +2164,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "943757300004366392" + "version": "0.29.47.4906", + "templateHash": "4327209924100539632" } }, "parameters": { @@ -2184,7 +2184,7 @@ "metadata": { "description": "Tags currently applied to the subscription level" }, - "value": "[if(contains(reference(resourceId('Microsoft.Resources/tags', parameters('name')), '2019-10-01'), 'tags'), reference(resourceId('Microsoft.Resources/tags', parameters('name')), '2019-10-01').tags, createObject())]" + "value": "[coalesce(tryGet(reference(resourceId('Microsoft.Resources/tags', parameters('name')), '2019-10-01'), 'tags'), createObject())]" } } } @@ -3487,8 +3487,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "15855049387961116888" + "version": "0.29.47.4906", + "templateHash": "3320668363507906643" } }, "parameters": { diff --git a/avm/ptn/lz/sub-vending/modules/readTagsResourceGroup.bicep b/avm/ptn/lz/sub-vending/modules/readTagsResourceGroup.bicep index 0f3301f974..b633ed6575 100644 --- a/avm/ptn/lz/sub-vending/modules/readTagsResourceGroup.bicep +++ b/avm/ptn/lz/sub-vending/modules/readTagsResourceGroup.bicep @@ -6,4 +6,4 @@ resource tags 'Microsoft.Resources/tags@2019-10-01' existing = { } @description('Tags currently applied to the subscription level') -output existingTags object = contains(tags.properties, 'tags') ? tags.properties.tags : {} +output existingTags object = tags.properties.?tags ?? {} diff --git a/avm/ptn/lz/sub-vending/modules/readTagsSubscription.bicep b/avm/ptn/lz/sub-vending/modules/readTagsSubscription.bicep index 65b2457259..6c9f04feef 100644 --- a/avm/ptn/lz/sub-vending/modules/readTagsSubscription.bicep +++ b/avm/ptn/lz/sub-vending/modules/readTagsSubscription.bicep @@ -8,4 +8,4 @@ resource tags 'Microsoft.Resources/tags@2019-10-01' existing = { } @description('Tags currently applied to the subscription level') -output existingTags object = contains(tags.properties, 'tags') ? tags.properties.tags : {} +output existingTags object = tags.properties.?tags ?? {} diff --git a/avm/ptn/lz/sub-vending/modules/subResourceWrapper.bicep b/avm/ptn/lz/sub-vending/modules/subResourceWrapper.bicep index 6a6e2e6195..bd8fddf190 100644 --- a/avm/ptn/lz/sub-vending/modules/subResourceWrapper.bicep +++ b/avm/ptn/lz/sub-vending/modules/subResourceWrapper.bicep @@ -295,7 +295,7 @@ var virtualWanHubSubscriptionId = (!empty(virtualHubResourceIdChecked) ? split(v var virtualWanHubResourceGroupName = (!empty(virtualHubResourceIdChecked) ? split(virtualHubResourceIdChecked, '/')[4] : '') -var virtualWanHubConnectionName = 'vhc-${guid(virtualHubResourceIdChecked, virtualNetworkName, virtualNetworkResourceGroupName, virtualNetworkLocation, subscriptionId)}' +var virtualWanHubConnectionName = 'vhc-${virtualNetworkName}-${substring(guid(virtualHubResourceIdChecked, virtualNetworkName, virtualNetworkResourceGroupName, virtualNetworkLocation, subscriptionId), 0, 5)}' var virtualWanHubConnectionAssociatedRouteTable = !empty(virtualNetworkVwanAssociatedRouteTableResourceId) ? virtualNetworkVwanAssociatedRouteTableResourceId : '${virtualHubResourceIdChecked}/hubRouteTables/defaultRouteTable' From dc730bd3d538764afd391beb2ba2a2b08949a6c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Thu, 12 Sep 2024 09:03:50 +0200 Subject: [PATCH 15/57] feat: Adds authentication to image import `ptn/deployment-script/import-image-to-acr` (#3253) ## Description Adds the option to authenticate to the source container registry. Used for e.g. docker hub login to avoid throttling. Closes #3069 ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.deployment-script.import-image-to-acr](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.ptn.deployment-script.import-image-to-acr.yml/badge.svg?branch=import-image-docker-authentication)](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.ptn.deployment-script.import-image-to-acr.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [x] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../import-image-to-acr/README.md | 33 ++- .../import-image-to-acr/main.bicep | 72 ++++--- .../import-image-to-acr/main.json | 191 +++++++++++------- .../tests/e2e/max/dependencies.bicep | 55 +++++ .../tests/e2e/max/main.test.bicep | 11 +- .../import-image-to-acr/version.json | 2 +- 6 files changed, 245 insertions(+), 119 deletions(-) diff --git a/avm/ptn/deployment-script/import-image-to-acr/README.md b/avm/ptn/deployment-script/import-image-to-acr/README.md index b079fe65c1..42038de2c6 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/README.md +++ b/avm/ptn/deployment-script/import-image-to-acr/README.md @@ -281,6 +281,8 @@ module importImageToAcr 'br/public:avm/ptn/deployment-script/import-image-to-acr | [`overwriteExistingImage`](#parameter-overwriteexistingimage) | bool | The image will be overwritten if it already exists in the ACR with the same tag. Default is false. | | [`retryMax`](#parameter-retrymax) | int | The maximum number of retries for the script import operation. Default is 3. | | [`runOnce`](#parameter-runonce) | bool | How the deployment script should be forced to execute. Default is to force the script to deploy the image to run every time. | +| [`sourceRegistryPassword`](#parameter-sourceregistrypassword) | securestring | The password for the source registry. Required if the source registry is private, or to logon to the public docker registry. | +| [`sourceRegistryUsername`](#parameter-sourceregistryusername) | string | The username for the source registry. Required if the source registry is private, or to logon to the public docker registry. | | [`storageAccountResourceId`](#parameter-storageaccountresourceid) | string | The resource id of the storage account to use for the deployment script. An existing storage account is needed, if PrivateLink is going to be used for the deployment script. | | [`subnetResourceIds`](#parameter-subnetresourceids) | array | The subnet ids to use for the deployment script. An existing subnet is needed, if PrivateLink is going to be used for the deployment script. | | [`tags`](#parameter-tags) | object | Tags of the resource. | @@ -298,7 +300,12 @@ A fully qualified image name to import. - Required: Yes - Type: string -- Example: `mcr.microsoft.com/k8se/quickstart-jobs:latest` +- Example: + ```Bicep + mcr.microsoft.com/k8se/quickstart-jobs:latest + docker.io/library/image:latest + docker.io/hello-world:latest + ``` ### Parameter: `name` @@ -415,6 +422,22 @@ How the deployment script should be forced to execute. Default is to force the s - Type: bool - Default: `False` +### Parameter: `sourceRegistryPassword` + +The password for the source registry. Required if the source registry is private, or to logon to the public docker registry. + +- Required: No +- Type: securestring +- Default: `''` + +### Parameter: `sourceRegistryUsername` + +The username for the source registry. Required if the source registry is private, or to logon to the public docker registry. + +- Required: No +- Type: string +- Default: `''` + ### Parameter: `storageAccountResourceId` The resource id of the storage account to use for the deployment script. An existing storage account is needed, if PrivateLink is going to be used for the deployment script. @@ -458,13 +481,17 @@ This section gives you an overview of all local-referenced module files (i.e., o | Reference | Type | | :-- | :-- | -| `br/public:avm/res/resources/deployment-script:0.2.3` | Remote reference | +| `br/public:avm/res/resources/deployment-script:0.4.0` | Remote reference | ## Notes The deployment script service will need and provision a Storage Account as well as a Container Instance to execute the provided script. _The deployment script resource is available only in the regions where Azure Container Instances is available._ -> The service cleans up these resources after the deployment script finishes. You incur charges for these resources until they're removed. +> The service cleans up these resources after the deployment script finishes. You incur charges for these resources until they are removed. + +### Authentication to source Container Registry + +Authentication is possible by setting the ```sourceRegistryUsername``` and ```sourceRegistryPassword``` parameters. An example that uses Key Vault is in the max sample. It is commented out, as for the shared environments no user exists, that could be used to access e.g. docker hub images. ### Private network access diff --git a/avm/ptn/deployment-script/import-image-to-acr/main.bicep b/avm/ptn/deployment-script/import-image-to-acr/main.bicep index eb578a27a1..64be560c36 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/main.bicep +++ b/avm/ptn/deployment-script/import-image-to-acr/main.bicep @@ -28,10 +28,21 @@ param managedIdentityName string? @description('Required. A fully qualified image name to import.') @metadata({ - example: 'mcr.microsoft.com/k8se/quickstart-jobs:latest' + example: [ + 'mcr.microsoft.com/k8se/quickstart-jobs:latest' + 'docker.io/library/image:latest' + 'docker.io/hello-world:latest' + ] }) param image string +@description('Optional. The username for the source registry. Required if the source registry is private, or to logon to the public docker registry.') +param sourceRegistryUsername string = '' + +@description('Optional. The password for the source registry. Required if the source registry is private, or to logon to the public docker registry.') +@secure() +param sourceRegistryPassword string = '' + @description('Optional. The new image name in the ACR. You can use this to import a publically available image with a custom name for later updating from e.g., your build pipeline.') @metadata({ example: 'your-image-name:tag' @@ -147,7 +158,7 @@ resource acrRoleAssignmentNewManagedIdentity 'Microsoft.Authorization/roleAssign } } -module imageImport 'br/public:avm/res/resources/deployment-script:0.2.3' = { +module imageImport 'br/public:avm/res/resources/deployment-script:0.4.0' = { name: name ?? 'ACR-Import-${last(split(replace(image,':','-'),'/'))}' scope: resourceGroup() params: { @@ -159,41 +170,20 @@ module imageImport 'br/public:avm/res/resources/deployment-script:0.2.3' = { : { userAssignedResourcesIds: [newManagedIdentity.id] } kind: 'AzureCLI' runOnce: runOnce - azCliVersion: '2.61.0' // available tags are listed here: https://mcr.microsoft.com/v2/azure-cli/tags/list + azCliVersion: '2.63.0' // available tags are listed here: https://mcr.microsoft.com/v2/azure-cli/tags/list timeout: 'PT30M' // set timeout to 30m retentionInterval: 'PT1H' // cleanup after 1h - environmentVariables: { - secureList: [ - { - name: 'acrName' - value: acrName - } - { - name: 'imageName' - value: image - } - { - name: 'newImageName' - value: newImageName - } - { - name: 'overwriteExistingImage' - value: toLower(string(overwriteExistingImage)) - } - { - name: 'initialDelay' - value: '${string(initialScriptDelay)}s' - } - { - name: 'retryMax' - value: string(retryMax) - } - { - name: 'retrySleep' - value: '5s' - } - ] - } + environmentVariables: [ + { name: 'acrName', value: acrName } + { name: 'imageName', value: image } + { name: 'newImageName', value: newImageName } + { name: 'overwriteExistingImage', value: toLower(string(overwriteExistingImage)) } + { name: 'initialDelay', value: '${string(initialScriptDelay)}s' } + { name: 'retryMax', value: string(retryMax) } + { name: 'retrySleep', value: '5s' } + { name: 'sourceRegistryUsername', value: sourceRegistryUsername } + { name: 'sourceRegistryPassword', secureValue: sourceRegistryPassword } + ] cleanupPreference: cleanupPreference storageAccountResourceId: storageAccountResourceId containerGroupName: '${resourceGroup().name}-infrastructure' @@ -210,9 +200,17 @@ module imageImport 'br/public:avm/res/resources/deployment-script:0.2.3' = { do echo "Importing Image ($retryLoopCount): $imageName into ACR: $acrName\n" if [ $overwriteExistingImage = 'true' ]; then - az acr import -n $acrName --source $imageName --image $newImageName --force + if [ -n "$sourceRegistryUsername" ] && [ -n "$sourceRegistryPassword" ]; then + az acr import -n $acrName --source $imageName --image $newImageName --force --username $sourceRegistryUsername --password $sourceRegistryPassword + else + az acr import -n $acrName --source $imageName --image $newImageName --force + fi else - az acr import -n $acrName --source $imageName --image $newImageName + if [ -n "$sourceRegistryUsername" ] && [ -n "$sourceRegistryPassword" ]; then + az acr import -n $acrName --source $imageName --image $newImageName --username $sourceRegistryUsername --password $sourceRegistryPassword + else + az acr import -n $acrName --source $imageName --image $newImageName + fi fi sleep $retrySleep diff --git a/avm/ptn/deployment-script/import-image-to-acr/main.json b/avm/ptn/deployment-script/import-image-to-acr/main.json index ffc946ccc3..51751e27f8 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/main.json +++ b/avm/ptn/deployment-script/import-image-to-acr/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "18410876545978102921" + "version": "0.29.47.4906", + "templateHash": "15179702678978782456" }, "name": "import-image-to-acr", "description": "This modules deployes an image to an Azure Container Registry.", @@ -103,10 +103,28 @@ "image": { "type": "string", "metadata": { - "example": "mcr.microsoft.com/k8se/quickstart-jobs:latest", + "example": [ + "mcr.microsoft.com/k8se/quickstart-jobs:latest", + "docker.io/library/image:latest", + "docker.io/hello-world:latest" + ], "description": "Required. A fully qualified image name to import." } }, + "sourceRegistryUsername": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The username for the source registry. Required if the source registry is private, or to logon to the public docker registry." + } + }, + "sourceRegistryPassword": { + "type": "securestring", + "defaultValue": "", + "metadata": { + "description": "Optional. The password for the source registry. Required if the source registry is private, or to logon to the public docker registry." + } + }, "newImageName": { "type": "string", "defaultValue": "[last(split(parameters('image'), '/'))]", @@ -289,7 +307,7 @@ "value": "[parameters('runOnce')]" }, "azCliVersion": { - "value": "2.61.0" + "value": "2.63.0" }, "timeout": { "value": "PT30M" @@ -298,38 +316,44 @@ "value": "PT1H" }, "environmentVariables": { - "value": { - "secureList": [ - { - "name": "acrName", - "value": "[parameters('acrName')]" - }, - { - "name": "imageName", - "value": "[parameters('image')]" - }, - { - "name": "newImageName", - "value": "[parameters('newImageName')]" - }, - { - "name": "overwriteExistingImage", - "value": "[toLower(string(parameters('overwriteExistingImage')))]" - }, - { - "name": "initialDelay", - "value": "[format('{0}s', string(parameters('initialScriptDelay')))]" - }, - { - "name": "retryMax", - "value": "[string(parameters('retryMax'))]" - }, - { - "name": "retrySleep", - "value": "5s" - } - ] - } + "value": [ + { + "name": "acrName", + "value": "[parameters('acrName')]" + }, + { + "name": "imageName", + "value": "[parameters('image')]" + }, + { + "name": "newImageName", + "value": "[parameters('newImageName')]" + }, + { + "name": "overwriteExistingImage", + "value": "[toLower(string(parameters('overwriteExistingImage')))]" + }, + { + "name": "initialDelay", + "value": "[format('{0}s', string(parameters('initialScriptDelay')))]" + }, + { + "name": "retryMax", + "value": "[string(parameters('retryMax'))]" + }, + { + "name": "retrySleep", + "value": "5s" + }, + { + "name": "sourceRegistryUsername", + "value": "[parameters('sourceRegistryUsername')]" + }, + { + "name": "sourceRegistryPassword", + "secureValue": "[parameters('sourceRegistryPassword')]" + } + ] }, "cleanupPreference": { "value": "[parameters('cleanupPreference')]" @@ -344,7 +368,7 @@ "value": "[parameters('subnetResourceIds')]" }, "scriptContent": { - "value": "#!/bin/bash\n set -e\n\n echo \"Waiting on RBAC replication ($initialDelay)\\n\"\n sleep $initialDelay\n\n # retry loop to catch errors (usually RBAC delays, but 'Error copying blobs' is also not unheard of)\n retryLoopCount=0\n until [ $retryLoopCount -ge $retryMax ]\n do\n echo \"Importing Image ($retryLoopCount): $imageName into ACR: $acrName\\n\"\n if [ $overwriteExistingImage = 'true' ]; then\n az acr import -n $acrName --source $imageName --image $newImageName --force\n else\n az acr import -n $acrName --source $imageName --image $newImageName\n fi\n\n sleep $retrySleep\n retryLoopCount=$((retryLoopCount+1))\n done\n\n echo \"done\\n\"" + "value": "#!/bin/bash\n set -e\n\n echo \"Waiting on RBAC replication ($initialDelay)\\n\"\n sleep $initialDelay\n\n # retry loop to catch errors (usually RBAC delays, but 'Error copying blobs' is also not unheard of)\n retryLoopCount=0\n until [ $retryLoopCount -ge $retryMax ]\n do\n echo \"Importing Image ($retryLoopCount): $imageName into ACR: $acrName\\n\"\n if [ $overwriteExistingImage = 'true' ]; then\n if [ -n \"$sourceRegistryUsername\" ] && [ -n \"$sourceRegistryPassword\" ]; then\n az acr import -n $acrName --source $imageName --image $newImageName --force --username $sourceRegistryUsername --password $sourceRegistryPassword\n else\n az acr import -n $acrName --source $imageName --image $newImageName --force\n fi\n else\n if [ -n \"$sourceRegistryUsername\" ] && [ -n \"$sourceRegistryPassword\" ]; then\n az acr import -n $acrName --source $imageName --image $newImageName --username $sourceRegistryUsername --password $sourceRegistryPassword\n else\n az acr import -n $acrName --source $imageName --image $newImageName\n fi\n fi\n\n sleep $retrySleep\n retryLoopCount=$((retryLoopCount+1))\n done\n\n echo \"done\\n\"" } }, "template": { @@ -354,8 +378,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "148060868388125113" + "version": "0.29.47.4906", + "templateHash": "5978422939896103340" }, "name": "Deployment Scripts", "description": "This module deploys Deployment Scripts.", @@ -407,6 +431,13 @@ "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -469,32 +500,29 @@ "nullable": true }, "environmentVariableType": { - "type": "secureObject", + "type": "object", "properties": { - "secureList": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "secureValue": { - "type": "string", - "nullable": true - }, - "value": { - "type": "string", - "nullable": true - } - } - }, + "name": { + "type": "string", "metadata": { - "description": "Optional. The list of environment variables to pass over to the deployment script." + "description": "Required. The name of the environment variable." + } + }, + "secureValue": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Required. The value of the secure environment variable." + } + }, + "value": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. The value of the environment variable." } } - }, - "nullable": true + } } }, "parameters": { @@ -564,9 +592,13 @@ } }, "environmentVariables": { - "$ref": "#/definitions/environmentVariableType", + "type": "array", + "items": { + "$ref": "#/definitions/environmentVariableType" + }, + "nullable": true, "metadata": { - "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." + "description": "Optional. The environment variables to pass over to the script." } }, "supportingScriptUris": { @@ -595,7 +627,7 @@ }, "retentionInterval": { "type": "string", - "nullable": true, + "defaultValue": "P1D", "metadata": { "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)." } @@ -669,6 +701,11 @@ }, "variables": { "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + }, { "name": "subnetIds", "count": "[length(coalesce(parameters('subnetResourceIds'), createArray()))]", @@ -681,7 +718,7 @@ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" }, "containerSettings": { @@ -705,7 +742,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.resources-deploymentscript.{0}.{1}', replace('0.2.3', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.resources-deploymentscript.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -735,7 +772,7 @@ "containerSettings": "[if(not(empty(variables('containerSettings'))), variables('containerSettings'), null())]", "storageAccountSettings": "[if(not(empty(parameters('storageAccountResourceId'))), if(not(empty(parameters('storageAccountResourceId'))), createObject('storageAccountKey', if(empty(parameters('subnetResourceIds')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '//'), '/')[2], split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), 'dummyAccount'), '/'))), '2023-01-01').keys[0].value, null()), 'storageAccountName', last(split(parameters('storageAccountResourceId'), '/'))), null()), null())]", "arguments": "[parameters('arguments')]", - "environmentVariables": "[if(not(equals(parameters('environmentVariables'), null())), parameters('environmentVariables').secureList, createArray())]", + "environmentVariables": "[parameters('environmentVariables')]", "scriptContent": "[if(not(empty(parameters('scriptContent'))), parameters('scriptContent'), null())]", "primaryScriptUri": "[if(not(empty(parameters('primaryScriptUri'))), parameters('primaryScriptUri'), null())]", "supportingScriptUris": "[if(not(empty(parameters('supportingScriptUris'))), parameters('supportingScriptUris'), null())]", @@ -765,20 +802,20 @@ "deploymentScript_roleAssignments": { "copy": { "name": "deploymentScript_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Resources/deploymentScripts/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Resources/deploymentScripts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Resources/deploymentScripts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ "deploymentScript" @@ -828,7 +865,7 @@ "metadata": { "description": "The output of the deployment script." }, - "value": "[if(contains(reference('deploymentScript'), 'outputs'), reference('deploymentScript').outputs, createObject())]" + "value": "[coalesce(tryGet(reference('deploymentScript'), 'outputs'), createObject())]" }, "deploymentScriptLogs": { "type": "array", diff --git a/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/dependencies.bicep b/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/dependencies.bicep index c66f2f19c2..f68b4451d5 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/dependencies.bicep +++ b/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/dependencies.bicep @@ -13,6 +13,9 @@ param acrName string @description('Required. The name of the Storage Account to create.') param storageAccountName string +@description('Required. The name of the Key Vault to create.') +param keyVaultName string + var ipRange = '10.0.0.0' module identity 'br/public:avm/res/managed-identity/user-assigned-identity:0.2.1' = { @@ -105,6 +108,52 @@ module storage 'br/public:avm/res/storage/storage-account:0.9.0' = { } } +// KeyVault stores the password to login to the source container registry +resource keyVault 'Microsoft.KeyVault/vaults@2023-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: [] + } + dependsOn: [identity] + + resource containerRegistrySecret 'secrets@2023-07-01' = { + name: 'ContainerRegistryPassword' + properties: { + // put the password of the source container registry here + value: '' + } + } + + resource rbac 'accessPolicies@2023-07-01' = { + name: 'add' + properties: { + accessPolicies: [ + { + tenantId: tenant().tenantId + objectId: identity.outputs.principalId + permissions: { + keys: [] + secrets: ['get', 'list', 'set'] + certificates: [] + storage: [] + } + } + ] + } + } +} + // the container registry to upload the image into module acr 'br/public:avm/res/container-registry/registry:0.2.0' = { name: '${uniqueString(resourceGroup().name, location)}-acr' @@ -145,3 +194,9 @@ output storageAccountResourceId string = storage.outputs.resourceId @description('The resource ID of the created subnet designated for the Deployment Script.') output deploymentScriptSubnetResourceId string = vnet::subnet_deploymentscript.id + +@description('The resource ID of the created Key Vault.') +output keyVaultResourceId string = keyVault.id + +@description('The name of the created Key Vault secret.') +output keyVaultSecretName string = keyVault::containerRegistrySecret.name diff --git a/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/main.test.bicep b/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/main.test.bicep index a00d32d62c..3c7e75c684 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/main.test.bicep +++ b/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/main.test.bicep @@ -31,6 +31,7 @@ module dependencies 'dependencies.bicep' = { virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' acrName: 'dep${namePrefix}acr${serviceShort}' storageAccountName: 'dep${namePrefix}sa${serviceShort}' + keyVaultName: 'dep${namePrefix}kv${serviceShort}' managedIdentityName: 'dep-${namePrefix}-mi-${serviceShort}' } } @@ -46,6 +47,11 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { // Test Execution // // ============== // +resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' existing = { + name: last(split(dependencies.outputs.keyVaultResourceId, '/')) + scope: resourceGroup +} + @batchSize(1) module testDeployment '../../../main.bicep' = [ for iteration in ['init', 'idem']: { @@ -59,7 +65,10 @@ module testDeployment '../../../main.bicep' = [ } acrName: dependencies.outputs.acrName location: resourceLocation - image: 'mcr.microsoft.com/k8se/quickstart-jobs:latest' + image: 'mcr.microsoft.com/k8se/quickstart-jobs:latest' // e.g. for docker images, that will be authenticated with the below properties 'docker.io/hello-world:latest' + // commented out, as the user is not available in the test environment + // sourceRegistryUsername: 'username' + // sourceRegistryPassword: keyVault.getSecret(dependencies.outputs.keyVaultSecretName) newImageName: 'your-image-name:tag' cleanupPreference: 'OnExpiration' assignRbacRole: true diff --git a/avm/ptn/deployment-script/import-image-to-acr/version.json b/avm/ptn/deployment-script/import-image-to-acr/version.json index daf1a794d9..17dd49a0b9 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/version.json +++ b/avm/ptn/deployment-script/import-image-to-acr/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.2", + "version": "0.3", "pathFilters": [ "./main.json" ] From 5e3198e9e9533aa74c786c9012ccd4fbe31c6cbc Mon Sep 17 00:00:00 2001 From: "Jianing Wang (MSFT)" <141212663+jianingwang123@users.noreply.github.com> Date: Thu, 12 Sep 2024 16:53:05 +0800 Subject: [PATCH 16/57] fix: Run the `Set-AVMModule` script to update the files of ptn module `azd/apim-api` (#3256) ## Description Fixes [comment](https://github.com/Azure/bicep-registry-modules/pull/3206#issuecomment-2341363647) adout static test failed of ptn modules `azd/apim-api`. ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.azd.apim-api](https://github.com/jianingwang123/bicep-registry-modules/actions/workflows/avm.ptn.azd.apim-api.yml/badge.svg?branch=fix%2Fnew)](https://github.com/jianingwang123/bicep-registry-modules/actions/workflows/avm.ptn.azd.apim-api.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings @jongio for notification. --- avm/ptn/azd/apim-api/README.md | 11 ++--------- avm/ptn/azd/apim-api/main.bicep | 2 +- avm/ptn/azd/apim-api/main.json | 4 ++-- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/avm/ptn/azd/apim-api/README.md b/avm/ptn/azd/apim-api/README.md index 681638af79..f86fc2befd 100644 --- a/avm/ptn/azd/apim-api/README.md +++ b/avm/ptn/azd/apim-api/README.md @@ -10,7 +10,6 @@ Creates and configure an API within an API Management service instance. - [Usage examples](#Usage-examples) - [Parameters](#Parameters) - [Outputs](#Outputs) -- [Cross-referenced modules](#Cross-referenced-modules) - [Data Collection](#Data-Collection) ## Resource Types @@ -104,7 +103,6 @@ module apimApi 'br/public:avm/ptn/azd/apim-api:' = {

- ## Parameters **Required parameters** @@ -117,7 +115,7 @@ module apimApi 'br/public:avm/ptn/azd/apim-api:' = { | [`apiName`](#parameter-apiname) | string | Resource name to uniquely identify this API within the API Management service instance. | | [`apiPath`](#parameter-apipath) | string | Relative URL uniquely identifying this API and all of its resource paths within the API Management service instance. It is appended to the API endpoint base URL specified during the service instance creation to form a public URL for this API. | | [`name`](#parameter-name) | string | Name of the API Management service instance. | -| [`webFrontendUrl`](#parameter-webfrontendurl) | string | Absolute URL of the web frontend. | +| [`webFrontendUrl`](#parameter-webfrontendurl) | string | Absolute URL of web frontend. | **Optional parameters** @@ -171,7 +169,7 @@ Name of the API Management service instance. ### Parameter: `webFrontendUrl` -Absolute URL of the web frontend. +Absolute URL of web frontend. - Required: Yes - Type: string @@ -200,7 +198,6 @@ Location for all Resources. - Type: string - Default: `[resourceGroup().location]` - ## Outputs | Output | Type | Description | @@ -208,10 +205,6 @@ Location for all Resources. | `resourceGroupName` | string | The name of the resource group. | | `serviceApiUri` | string | The complete URL for accessing the API. | -## Cross-referenced modules - -_None_ - ## Data Collection The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/ptn/azd/apim-api/main.bicep b/avm/ptn/azd/apim-api/main.bicep index 22c2fd5435..becb6b512e 100644 --- a/avm/ptn/azd/apim-api/main.bicep +++ b/avm/ptn/azd/apim-api/main.bicep @@ -24,7 +24,7 @@ param apiDescription string @minLength(1) param apiPath string -@description('Required. Absolute URL of the web frontend.') +@description('Required. Absolute URL of web frontend.') param webFrontendUrl string @description('Optional. Location for all Resources.') diff --git a/avm/ptn/azd/apim-api/main.json b/avm/ptn/azd/apim-api/main.json index dacd440b8a..912dbe4412 100644 --- a/avm/ptn/azd/apim-api/main.json +++ b/avm/ptn/azd/apim-api/main.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "7935494033060946539" + "templateHash": "1542387667896789833" }, "name": "avm/ptn/azd/apim-api", "description": "Creates and configure an API within an API Management service instance.\n\n**Note:** This module is not intended for broad, generic use, as it was designed to cater for the requirements of the AZD CLI product. Feature requests and bug fix requests are welcome if they support the development of the AZD CLI but may not be incorporated if they aim to make this module more generic than what it needs to be for its primary use case.", @@ -50,7 +50,7 @@ "webFrontendUrl": { "type": "string", "metadata": { - "description": "Required. Absolute URL of the web frontend." + "description": "Required. Absolute URL of web frontend." } }, "location": { From ada1720a43569524d061a12d7d66b21a4a70a362 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Thu, 12 Sep 2024 17:41:39 +0200 Subject: [PATCH 17/57] feat: Added utility to re-run failed jobs for failed workflows (#2968) ## Description As it happens from time to time that multiple workflows fail due to temporary issues (e.g. PSGallery not responding, throttling, etc.), this utility is intended to easy re-running the failed jobs for these workflows. By default, the utility focuses on the `main` branch and once it ran, re-triggered all jobs, those jobs concluded, you can run the issue managing workflow to close the corresponding issues if the re-run fix the temporary issue. Example excution ```pwsh Invoke-FailedWorkflowsReRun # VERBOSE: Fetching current GitHub workflows # VERBOSE: Fetched [145] workflows # VERBOSE: Runs to re-run failed jobs for [6/145] # VERBOSE: Re-triggerung complete ``` ## Type of Change - [x] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --- .../tools/Invoke-WorkflowsFailedJobsReRun.ps1 | 415 ++++++++++++++++++ 1 file changed, 415 insertions(+) create mode 100644 avm/utilities/tools/Invoke-WorkflowsFailedJobsReRun.ps1 diff --git a/avm/utilities/tools/Invoke-WorkflowsFailedJobsReRun.ps1 b/avm/utilities/tools/Invoke-WorkflowsFailedJobsReRun.ps1 new file mode 100644 index 0000000000..adf77897e3 --- /dev/null +++ b/avm/utilities/tools/Invoke-WorkflowsFailedJobsReRun.ps1 @@ -0,0 +1,415 @@ +#region helper functions +<# +.SYNOPSIS +Get a list of all GitHub module workflows + +.DESCRIPTION +Get a list of all GitHub module workflows. Does not return all properties but only the relevant ones. + +.PARAMETER PersonalAccessToken +Optional. The PAT to use to interact with either GitHub / Azure DevOps. If not provided, the script will use the GitHub CLI to authenticate. + +.PARAMETER RepositoryOwner +Mandatory. The repository's organization. + +.PARAMETER RepositoryName +Mandatory. The name of the repository to fetch the workflows from. + +.PARAMETER IncludeDisabled +Optional. Set if you want to also include disabled workflows in the result. + +.PARAMETER Filter +Optional. A regex filter to apply when fetching the workflows. By default we fetch all module workflows. + +.EXAMPLE +Get-GitHubModuleWorkflowList -PersonalAccessToken '' -RepositoryOwner 'Azure' -RepositoryName 'bicep-registry-modules' + +Get all module workflows from repository 'Azure/bicep-registry-modules' +#> +function Get-GitHubModuleWorkflowList { + + [CmdletBinding()] + param ( + [Parameter(Mandatory = $false)] + [string] $PersonalAccessToken, + + [Parameter(Mandatory = $true)] + [string] $RepositoryOwner, + + [Parameter(Mandatory = $true)] + [string] $RepositoryName, + + [Parameter(Mandatory = $false)] + [switch] $IncludeDisabled, + + [Parameter(Mandatory = $false)] + [string] $Filter = 'avm\.(?:res|ptn|utl)' + ) + + $allWorkflows = @() + + $page = 1 + do { + $queryUrl = "/repos/$RepositoryOwner/$RepositoryName/actions/workflows?per_page=100&page=$page" + if ($PersonalAccessToken) { + # Using PAT + $requestInputObject = @{ + Method = 'GET' + Uri = "https://api.github.com$queryUrl" + Headers = @{ + Authorization = "Bearer $PersonalAccessToken" + } + } + $response = Invoke-RestMethod @requestInputObject + } else { + # Using GH API instead of 'gh workflow list' to get all results instead of just the first few + $requestInputObject = @( + '-H', 'Accept: application/vnd.github+json', + '-H', 'X-GitHub-Api-Version: 2022-11-28', + $queryUrl + ) + $response = (gh api @requestInputObject | ConvertFrom-Json) + } + + if (-not $response.workflows) { + Write-Error "Request failed. Reponse: [$response]" + } + + $allWorkflows += $response.workflows | Select-Object -Property @('id', 'name', 'path', 'badge_url', 'state') | Where-Object { + $_.name -match $Filter -and + ($IncludeDisabled ? $true : $_.state -eq 'active') + } + + $expectedPages = [math]::ceiling($response.total_count / 100) + $page++ + } while ($page -le $expectedPages) + + return $allWorkflows +} + + +<# +.SYNOPSIS +Invoke the re-run for a given set of workflow runs. + +.DESCRIPTION +Invoke the re-run for a given set of workflow runs. + +.PARAMETER RestInputObject +Mandatory. The REST parameters to use for the re-run. Must contain the 'RepositoryOwner' and 'RepositoryName' keys and may contain the 'PersonalAccessToken' key. + +.PARAMETER RunsToReTrigger +Manadatory. The workflow runs to re-trigger. + +.PARAMETER TotalNumberOfWorkflows +Mandatory. The total number of workflows to re-trigger. + +.EXAMPLE +Invoke-ReRun -RestInputObject @{ RepositoryOwner = 'Azure'; RepositoryName = 'bicep-registry-modules' } -RunsToReTrigger @(@{ id = 123; name = 'keyvaultworkflow'}) -TotalNumberOfWorkflows 123 + +Re-run the failed jobs for all provided runs in the repository [Azure/bicep-registry-modules]. +#> +function Invoke-ReRun { + + [CmdletBinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory)] + [hashtable] $RestInputObject, + + [Parameter(Mandatory)] + [object[]] $RunsToReTrigger, + + [Parameter(Mandatory)] + [int] $TotalNumberOfWorkflows + ) + + $totalCount = $RunsToReTrigger.Count + $currentCount = 1 + Write-Verbose ('Runs to re-run failed jobs for [{0}/{1}]' -f $RunsToReTrigger.Count, $TotalNumberOfWorkflows) -Verbose + foreach ($run in $RunsToReTrigger) { + $percentageComplete = [math]::Round(($currentCount / $totalCount) * 100) + Write-Progress -Activity ('Re-running failed jobs for workflow [{0}]' -f $run.name) -Status "$percentageComplete% complete" -PercentComplete $percentageComplete + + if ($PSCmdlet.ShouldProcess(("Re-run of failed jobs for GitHub workflow [{0}] for branch [$TargetBranch]" -f $run.name), 'Invoke')) { + $null = Invoke-GitHubWorkflowRunFailedJobsReRun @RestInputObject -RunId $run.id + } + $currentCount++ + } +} + +<# +.SYNOPSIS +Get the latest run of a GitHub workflow for a given branch. + +.DESCRIPTION +Get the latest run of a GitHub workflow for a given branch. + +.PARAMETER PersonalAccessToken +Optional. The PAT to use to interact with either GitHub. If not provided, the script will use the GitHub CLI to authenticate. + +.PARAMETER RepositoryOwner +Optional. The GitHub organization the workfows are located in. + +.PARAMETER RepositoryName +Optional. The GitHub repository the workfows are located in. + +.PARAMETER WorkflowId +Required. The ID of the workflow to get the latest run for. + +.PARAMETER TargetBranch +Optional. The branch to get the latest run for. Defaults to 'main'. + +.EXAMPLE +Get-GitHubModuleWorkflowLatestRun -RepositoryOwner 'Azure' -RepositoryName 'bicep-registry-modules' -WorkflowId '447791597' + +Get the latest workflow run of the repository [Azure/bicep-registry-modules] for a workflow with id '447791597', filtered to the 'main' branch. +#> +function Get-GitHubModuleWorkflowLatestRun { + + [CmdletBinding()] + param ( + [Parameter(Mandatory = $false)] + [string] $PersonalAccessToken, + + [Parameter(Mandatory = $true)] + [string] $RepositoryOwner, + + [Parameter(Mandatory = $true)] + [string] $RepositoryName, + + [Parameter(Mandatory = $true)] + [string] $WorkflowId, + + [Parameter(Mandatory = $false)] + [string] $TargetBranch = 'main' + ) + + $queryUrl = "/repos/$RepositoryOwner/$RepositoryName/actions/workflows/$WorkflowId/runs?branch=$TargetBranch&per_page=1" + if ($PersonalAccessToken) { + # Using PAT + $requestInputObject = @{ + Method = 'GET' + Uri = "https://api.github.com$queryUrl" + Headers = @{ + Authorization = "Bearer $PersonalAccessToken" + } + } + $response = Invoke-RestMethod @requestInputObject + } else { + # Using GH API instead of 'gh workflow list' to get all results instead of just the first few + $requestInputObject = @( + '-H', 'Accept: application/vnd.github+json', + '-H', 'X-GitHub-Api-Version: 2022-11-28', + $queryUrl + ) + $response = (gh api @requestInputObject | ConvertFrom-Json) + } + + if (-not $response.workflow_runs) { + Write-Error "Request failed. Reponse: [$response]" + } + + return $response.workflow_runs | Select-Object -Property @('id', 'name', 'path', 'status', 'head_branch', 'created_at', 'run_number', 'run_attempt', 'conclusion') +} + +<# +.SYNOPSIS +Invoke the 'Rerun failed jobs' action for a given GitHub workflow run. + +.DESCRIPTION +Invoke the 'Rerun failed jobs' action for a given GitHub workflow run. + +.PARAMETER PersonalAccessToken +Optional. The PAT to use to interact with either GitHub. If not provided, the script will use the GitHub CLI to authenticate. + +.PARAMETER RepositoryOwner +Optional. The GitHub organization to run the workfows in. + +.PARAMETER RepositoryName +Optional. The GitHub repository to run the workfows in. + +.PARAMETER RunId +Mandatory. The ID of the run to re-run the failed jobs for. + +.EXAMPLE +Invoke-GitHubWorkflowRunFailedJobsReRun -RepositoryOwner 'Azure' -RepositoryName 'bicep-registry-modules' -RunId '447791597' + +Re-run the failed jobs for the GitHub workflow run with ID '447791597' in the repository [Azure/bicep-registry-modules]. +#> +function Invoke-GitHubWorkflowRunFailedJobsReRun { + + [CmdletBinding()] + param ( + [Parameter(Mandatory = $false)] + [string] $PersonalAccessToken, + + [Parameter(Mandatory = $true)] + [string] $RepositoryOwner, + + [Parameter(Mandatory = $true)] + [string] $RepositoryName, + + [Parameter(Mandatory = $true)] + [string] $RunId + ) + + + $queryUrl = "/repos/$RepositoryOwner/$RepositoryName/actions/runs/$RunId/rerun-failed-jobs" + if ($PersonalAccessToken) { + # Using PAT + $requestInputObject = @{ + Method = 'POST' + Uri = "https://api.github.com$queryUrl" + Headers = @{ + Authorization = "Bearer $PersonalAccessToken" + } + } + $response = Invoke-RestMethod @requestInputObject + } else { + # Using GH API instead of 'gh workflow list' to get all results instead of just the first few + $requestInputObject = @( + '--method', 'POST', + '-H', 'Accept: application/vnd.github+json', + '-H', 'X-GitHub-Api-Version: 2022-11-28', + $queryUrl + ) + $response = (gh api @requestInputObject | ConvertFrom-Json) + } + + if ("$response") { + # If successfull, the response will be an empty custom object. Must be casted to string + Write-Error "Request failed. Response: [$response]" + return $false + } + + return $true +} +#endregion + +<# +.SYNOPSIS +Re-runs all failed jobs across all workflows for a given GitHub repository. + +.DESCRIPTION +Re-runs all failed jobs across all workflows for a given GitHub repository. +By default, pipelines are filtered to AVM module pipelines & the main branch. +Currently running workflows are excluded. + +.PARAMETER PersonalAccessToken +Optional. The PAT to use to interact with either GitHub. If not provided, the script will use the GitHub CLI to authenticate. + +.PARAMETER TargetBranch +Optional. The branch to run the pipelines for (e.g. `main`). Defaults to 'main'. + +.PARAMETER PipelineFilter +Optional. The pipeline files to filter down to (regex). + +.PARAMETER RepositoryOwner +Optional. The GitHub organization to run the workfows in. + +.PARAMETER RepositoryName +Optional. The GitHub repository to run the workfows in. + +.EXAMPLE +Invoke-WorkflowsFailedJobsReRun -PersonalAccessToken '' -TargetBranch 'feature/branch' -PipelineFilter 'avm\.(?:res|ptn|utl)' + +Run the failed jobs for all GitHub workflows that match 'avm\.(?:res|ptn|utl)' using branch 'feature/branch'. + +.EXAMPLE +Invoke-WorkflowsFailedJobsReRun -PersonalAccessToken '' -TargetBranch 'feature/branch' -PipelineFilter 'avm\.(?:res|ptn|utl)' -WhatIf + +Only simulate the triggering of the failed jobs for all failed GitHub workflows that match 'avm\.(?:res|ptn|utl)' using branch 'feature/branch'. + +.EXAMPLE +Invoke-WorkflowsFailedJobsReRun -PersonalAccessToken '' -RepositoryOwner 'MyFork' + +Only simulate the triggering of the failed jobs of all GitHub workflows of project [MyFork/bicep-registry-modules] that start with'avm.res.res|ptn|utl', using the main branch & PAT. + +.EXAMPLE +Invoke-WorkflowsFailedJobsReRun -RepositoryOwner 'MyFork' + +Only simulate the triggering of the failed jobs of all GitHub workflows of project [MyFork/bicep-registry-modules] that start with'avm.res.res|ptn|utl', using the main branch & your current GH CLI login. +#> +function Invoke-WorkflowsFailedJobsReRun { + + [CmdletBinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory = $false)] + [string] $PersonalAccessToken, + + [Parameter(Mandatory = $false)] + [string] $TargetBranch = 'main', + + [Parameter(Mandatory = $false)] + [string] $PipelineFilter = 'avm\.(?:res|ptn|utl)', + + [Parameter(Mandatory = $false)] + [string] $RepositoryOwner = 'Azure', + + [Parameter(Mandatory = $false)] + [string] $RepositoryName = 'bicep-registry-modules' + ) + + $baseInputObject = @{ + RepositoryOwner = $RepositoryOwner + RepositoryName = $RepositoryName + } + if ($PersonalAccessToken) { + $baseInputObject['PersonalAccessToken'] = @{ + PersonalAccessToken = $PersonalAccessToken + } + } + ##################################### + # Get all workflows for branch # + ##################################### + Write-Verbose 'Fetching current GitHub workflows' -Verbose + $workflows = Get-GitHubModuleWorkflowList @baseInputObject -Filter $PipelineFilter + Write-Verbose ('Fetched [{0}] workflows' -f $workflows.Count) -Verbose + + + ###################################################### + # Analyze latest run of each workflow for branch # + ###################################################### + $totalCount = $workflows.Count + $currentCount = 1 + $runsToReTrigger = [System.Collections.ArrayList]@() + foreach ($workflow in $workflows) { + + $percentageComplete = [math]::Round(($currentCount / $totalCount) * 100) + Write-Progress -Activity ('Analyzing workflow [{0}]' -f $workflow.name) -Status "$percentageComplete% complete" -PercentComplete $percentageComplete + # Get relevant runs + $latestBranchRun = Get-GitHubModuleWorkflowLatestRun @baseInputObject -WorkflowId $workflow.id -TargetBranch $TargetBranch + + if ($latestBranchRun.status -eq 'completed' -and $latestBranchRun.conclusion -eq 'failure') { + $runsToReTrigger += $latestBranchRun + } + $currentCount++ + } + + ############################## + # Re-trigger failed runs # + ############################## + $reRunInputObject = @{ + RestInputObject = $baseInputObject + RunsToReTrigger = $runsToReTrigger + TotalNumberOfWorkflows = $workflows.Count + } + $null = Invoke-ReRun @reRunInputObject -WhatIf:$WhatIfPreference + + # Enable the user to execute the invocation if the whatif looked good + if ($WhatIfPreference) { + do { + $userInput = Read-Host -Prompt 'Should apply (y/n)?' + } + while ($userInput -notin @('y', 'n')) + + switch ($userInput) { + 'y' { + $null = Invoke-ReRun @reRunInputObject -WhatIf:$false + } + 'n' { return } + } + } + + Write-Verbose 'Re-triggerung complete' -Verbose +} From 497ffaf537fcda577eb1704d10199487c8d9ee95 Mon Sep 17 00:00:00 2001 From: hundredacres Date: Thu, 12 Sep 2024 23:29:01 -0700 Subject: [PATCH 18/57] feat: New module `avm/ptn/network/hub-networking` (#1257) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description New pattern module for hub networking. ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.network.hub-networking](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.ptn.network.hub-networking.yml/badge.svg?branch=hubspoke)](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.ptn.network.hub-networking.yml) | ## Type of Change Adding a new module - [x] A proposal has been submitted and approved. - [ ] I have included "Closes #{module_proposal_issue_number}" in the PR description. - [ ] I have run brm validate locally to verify the module files. - [X] I have run deployment tests locally to ensure the module is deployable. ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: René Hézser Co-authored-by: Kris Baranek Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> Co-authored-by: Ahmad Abdalla <28486158+ahmadabdalla@users.noreply.github.com> Co-authored-by: Alexander Sehr Co-authored-by: Clint Grove <30802291+clintgrove@users.noreply.github.com> Co-authored-by: Rainer Halanek <61878316+rahalan@users.noreply.github.com> Co-authored-by: ChrisSidebotham-MSFT <48600046+ChrisSidebotham@users.noreply.github.com> Co-authored-by: Sebastian Gräf Co-authored-by: Felix Borst <17405838+fblix@users.noreply.github.com> Co-authored-by: Felix Borst Co-authored-by: John Co-authored-by: Shenglong Li Co-authored-by: elisa anzelmo Co-authored-by: Máté Barabás Co-authored-by: Tao Yang Co-authored-by: Buddy <38195643+tsc-buddy@users.noreply.github.com> Co-authored-by: Ilhaan Rasheed Co-authored-by: Jack Tracey <41163455+jtracey93@users.noreply.github.com> Co-authored-by: JFolberth --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + .../avm.ptn.network.hub-networking.yml | 83 + .vscode/settings.json | 1 + avm/ptn/network/hub-networking/README.md | 1680 ++++ avm/ptn/network/hub-networking/main.bicep | 515 ++ avm/ptn/network/hub-networking/main.json | 6899 +++++++++++++++++ .../hub-networking/modules/getSubnet.bicep | 23 + .../hub-networking/modules/subnets.bicep | 185 + .../hub-networking/modules/vnets.bicep | 22 + .../tests/e2e/defaults/main.test.bicep | 47 + .../tests/e2e/max/main.test.bicep | 225 + .../tests/e2e/waf-aligned/main.test.bicep | 146 + avm/ptn/network/hub-networking/version.json | 7 + 14 files changed, 9835 insertions(+) create mode 100644 .github/workflows/avm.ptn.network.hub-networking.yml create mode 100644 avm/ptn/network/hub-networking/README.md create mode 100644 avm/ptn/network/hub-networking/main.bicep create mode 100644 avm/ptn/network/hub-networking/main.json create mode 100644 avm/ptn/network/hub-networking/modules/getSubnet.bicep create mode 100644 avm/ptn/network/hub-networking/modules/subnets.bicep create mode 100644 avm/ptn/network/hub-networking/modules/vnets.bicep create mode 100644 avm/ptn/network/hub-networking/tests/e2e/defaults/main.test.bicep create mode 100644 avm/ptn/network/hub-networking/tests/e2e/max/main.test.bicep create mode 100644 avm/ptn/network/hub-networking/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/ptn/network/hub-networking/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 69fd75e5c7..2bdd9e91d3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -19,6 +19,7 @@ /avm/ptn/dev-ops/cicd-agents-and-runners/ @Azure/avm-ptn-devops-cicdagentsandrunners-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/finops-toolkit/finops-hub/ @Azure/avm-ptn-finopstoolkit-finopshub-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/lz/sub-vending/ @Azure/avm-ptn-lz-subvending-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/ptn/network/hub-networking/ @Azure/avm-ptn-network-hubnetworking-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/network/private-link-private-dns-zones/ @Azure/avm-ptn-network-privatelinkprivatednszones-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/policy-insights/remediation/ @Azure/avm-ptn-policyinsights-remediation-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/security/security-center/ @Azure/avm-ptn-security-securitycenter-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index 76f4173ed1..fccb58bce5 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -54,6 +54,7 @@ body: - "avm/ptn/dev-ops/cicd-agents-and-runners" - "avm/ptn/finops-toolkit/finops-hub" - "avm/ptn/lz/sub-vending" + - "avm/ptn/network/hub-networking" - "avm/ptn/network/private-link-private-dns-zones" - "avm/ptn/policy-insights/remediation" - "avm/ptn/security/security-center" diff --git a/.github/workflows/avm.ptn.network.hub-networking.yml b/.github/workflows/avm.ptn.network.hub-networking.yml new file mode 100644 index 0000000000..617646aa74 --- /dev/null +++ b/.github/workflows/avm.ptn.network.hub-networking.yml @@ -0,0 +1,83 @@ +name: "avm.ptn.network.hub-networking" +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.ptn.network.hub-networking.yml" + - "avm/ptn/network/hub-networking/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" +env: + modulePath: "avm/ptn/network/hub-networking" + workflowPath: ".github/workflows/avm.ptn.network.hub-networking.yml" +concurrency: + group: ${{ github.workflow }} +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/.vscode/settings.json b/.vscode/settings.json index 0e02c76f6c..05509ca25f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "editor.formatOnSave": true, + "editor.bracketPairColorization.enabled": true, "files.trimTrailingWhitespace": true, "files.autoSave": "onFocusChange", "files.eol": "\n", diff --git a/avm/ptn/network/hub-networking/README.md b/avm/ptn/network/hub-networking/README.md new file mode 100644 index 0000000000..46c009b8c0 --- /dev/null +++ b/avm/ptn/network/hub-networking/README.md @@ -0,0 +1,1680 @@ +# Hub Networking `[Network/HubNetworking]` + +This module is designed to simplify the creation of multi-region hub networks in Azure. It will create a number of virtual networks and subnets, and optionally peer them together in a mesh topology with routing. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.Network/azureFirewalls` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/azureFirewalls) | +| `Microsoft.Network/bastionHosts` | [2022-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-11-01/bastionHosts) | +| `Microsoft.Network/publicIPAddresses` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-09-01/publicIPAddresses) | +| `Microsoft.Network/routeTables` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/routeTables) | +| `Microsoft.Network/routeTables/routes` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/routeTables/routes) | +| `Microsoft.Network/virtualNetworks` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks) | +| `Microsoft.Network/virtualNetworks/subnets` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks/subnets) | +| `Microsoft.Network/virtualNetworks/subnets` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualNetworks/subnets) | +| `Microsoft.Network/virtualNetworks/virtualNetworkPeerings` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks/virtualNetworkPeerings) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/ptn/network/hub-networking:`. + +- [Using only defaults](#example-1-using-only-defaults) +- [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module hubNetworking 'br/public:avm/ptn/network/hub-networking:' = { + name: 'hubNetworkingDeployment' + params: { + location: '' + } +} +``` + +
+

+ +

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

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

+ +via Bicep module + +```bicep +module hubNetworking 'br/public:avm/ptn/network/hub-networking:' = { + name: 'hubNetworkingDeployment' + params: { + hubVirtualNetworks: { + hub1: { + addressPrefixes: '' + azureFirewallSettings: { + azureSkuTier: 'Standard' + enableTelemetry: true + location: '' + publicIPAddressObject: { + name: 'hub1-waf-pip' + } + threatIntelMode: 'Alert' + } + bastionHost: { + disableCopyPaste: true + enableFileCopy: false + enableIpConnect: false + enableShareableLink: false + scaleUnits: 2 + skuName: 'Standard' + } + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + dnsServers: [ + '10.0.1.4' + '10.0.1.5' + ] + enableAzureFirewall: true + enableBastion: true + enablePeering: false + enableTelemetry: true + flowTimeoutInMinutes: 30 + location: '' + lock: { + kind: 'CanNotDelete' + name: 'hub1Lock' + } + peeringSettings: [ + { + allowForwardedTraffic: true + allowGatewayTransit: false + allowVirtualNetworkAccess: true + remoteVirtualNetworkName: 'hub2' + useRemoteGateways: false + } + ] + routes: [ + { + name: 'defaultRoute' + properties: { + addressPrefix: '0.0.0.0/0' + nextHopType: 'Internet' + } + } + ] + subnets: [ + { + addressPrefix: '' + name: 'GatewaySubnet' + } + { + addressPrefix: '' + name: 'AzureFirewallSubnet' + } + { + addressPrefix: '' + name: 'AzureBastionSubnet' + } + ] + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + vnetEncryption: false + vnetEncryptionEnforcement: 'AllowUnencrypted' + } + hub2: { + addressPrefixes: '' + azureFirewallSettings: { + azureSkuTier: 'Standard' + enableTelemetry: true + location: '' + publicIPAddressObject: { + name: 'hub2-waf-pip' + } + threatIntelMode: 'Alert' + zones: [ + 1 + 2 + 3 + ] + } + bastionHost: { + disableCopyPaste: true + enableFileCopy: false + enableIpConnect: false + enableShareableLink: false + scaleUnits: 2 + skuName: 'Standard' + } + enableAzureFirewall: true + enableBastion: true + enablePeering: false + enableTelemetry: false + flowTimeoutInMinutes: 10 + location: '' + lock: { + kind: 'CanNotDelete' + name: 'hub2Lock' + } + peeringSettings: [ + { + allowForwardedTraffic: true + allowGatewayTransit: false + allowVirtualNetworkAccess: true + remoteVirtualNetworkName: 'hub1' + useRemoteGateways: false + } + ] + routes: [ + { + name: 'defaultRoute' + properties: { + addressPrefix: '0.0.0.0/0' + nextHopType: 'Internet' + } + } + ] + subnets: [ + { + addressPrefix: '' + name: 'GatewaySubnet' + } + { + addressPrefix: '' + name: 'AzureFirewallSubnet' + } + { + addressPrefix: '' + name: 'AzureBastionSubnet' + } + ] + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + vnetEncryption: false + vnetEncryptionEnforcement: 'AllowUnencrypted' + } + } + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "hubVirtualNetworks": { + "value": { + "hub1": { + "addressPrefixes": "", + "azureFirewallSettings": { + "azureSkuTier": "Standard", + "enableTelemetry": true, + "location": "", + "publicIPAddressObject": { + "name": "hub1-waf-pip" + }, + "threatIntelMode": "Alert" + }, + "bastionHost": { + "disableCopyPaste": true, + "enableFileCopy": false, + "enableIpConnect": false, + "enableShareableLink": false, + "scaleUnits": 2, + "skuName": "Standard" + }, + "diagnosticSettings": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ], + "dnsServers": [ + "10.0.1.4", + "10.0.1.5" + ], + "enableAzureFirewall": true, + "enableBastion": true, + "enablePeering": false, + "enableTelemetry": true, + "flowTimeoutInMinutes": 30, + "location": "", + "lock": { + "kind": "CanNotDelete", + "name": "hub1Lock" + }, + "peeringSettings": [ + { + "allowForwardedTraffic": true, + "allowGatewayTransit": false, + "allowVirtualNetworkAccess": true, + "remoteVirtualNetworkName": "hub2", + "useRemoteGateways": false + } + ], + "routes": [ + { + "name": "defaultRoute", + "properties": { + "addressPrefix": "0.0.0.0/0", + "nextHopType": "Internet" + } + } + ], + "subnets": [ + { + "addressPrefix": "", + "name": "GatewaySubnet" + }, + { + "addressPrefix": "", + "name": "AzureFirewallSubnet" + }, + { + "addressPrefix": "", + "name": "AzureBastionSubnet" + } + ], + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + }, + "vnetEncryption": false, + "vnetEncryptionEnforcement": "AllowUnencrypted" + }, + "hub2": { + "addressPrefixes": "", + "azureFirewallSettings": { + "azureSkuTier": "Standard", + "enableTelemetry": true, + "location": "", + "publicIPAddressObject": { + "name": "hub2-waf-pip" + }, + "threatIntelMode": "Alert", + "zones": [ + 1, + 2, + 3 + ] + }, + "bastionHost": { + "disableCopyPaste": true, + "enableFileCopy": false, + "enableIpConnect": false, + "enableShareableLink": false, + "scaleUnits": 2, + "skuName": "Standard" + }, + "enableAzureFirewall": true, + "enableBastion": true, + "enablePeering": false, + "enableTelemetry": false, + "flowTimeoutInMinutes": 10, + "location": "", + "lock": { + "kind": "CanNotDelete", + "name": "hub2Lock" + }, + "peeringSettings": [ + { + "allowForwardedTraffic": true, + "allowGatewayTransit": false, + "allowVirtualNetworkAccess": true, + "remoteVirtualNetworkName": "hub1", + "useRemoteGateways": false + } + ], + "routes": [ + { + "name": "defaultRoute", + "properties": { + "addressPrefix": "0.0.0.0/0", + "nextHopType": "Internet" + } + } + ], + "subnets": [ + { + "addressPrefix": "", + "name": "GatewaySubnet" + }, + { + "addressPrefix": "", + "name": "AzureFirewallSubnet" + }, + { + "addressPrefix": "", + "name": "AzureBastionSubnet" + } + ], + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + }, + "vnetEncryption": false, + "vnetEncryptionEnforcement": "AllowUnencrypted" + } + } + }, + "location": { + "value": "" + } + } +} +``` + +
+

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

+ +via Bicep module + +```bicep +module hubNetworking 'br/public:avm/ptn/network/hub-networking:' = { + name: 'hubNetworkingDeployment' + params: { + hubVirtualNetworks: { + hub1: { + addressPrefixes: '' + azureFirewallSettings: { + azureSkuTier: 'Standard' + enableTelemetry: true + location: '' + publicIPAddressObject: { + name: 'hub1PublicIp' + } + threatIntelMode: 'Alert' + zones: [ + 1 + 2 + 3 + ] + } + bastionHost: { + disableCopyPaste: true + enableFileCopy: false + enableIpConnect: false + enableShareableLink: false + scaleUnits: 2 + skuName: 'Standard' + } + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + dnsServers: [ + '10.0.1.6' + '10.0.1.7' + ] + enableAzureFirewall: true + enableBastion: true + enablePeering: false + enableTelemetry: true + flowTimeoutInMinutes: 30 + location: '' + lock: { + kind: 'CanNotDelete' + name: 'hub1Lock' + } + routes: [ + { + name: 'defaultRoute' + properties: { + addressPrefix: '0.0.0.0/0' + nextHopType: 'Internet' + } + } + ] + subnets: [ + { + addressPrefix: '' + name: 'GatewaySubnet' + } + { + addressPrefix: '' + name: 'AzureFirewallSubnet' + } + { + addressPrefix: '' + name: 'AzureBastionSubnet' + } + ] + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + vnetEncryption: false + vnetEncryptionEnforcement: 'AllowUnencrypted' + } + } + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "hubVirtualNetworks": { + "value": { + "hub1": { + "addressPrefixes": "", + "azureFirewallSettings": { + "azureSkuTier": "Standard", + "enableTelemetry": true, + "location": "", + "publicIPAddressObject": { + "name": "hub1PublicIp" + }, + "threatIntelMode": "Alert", + "zones": [ + 1, + 2, + 3 + ] + }, + "bastionHost": { + "disableCopyPaste": true, + "enableFileCopy": false, + "enableIpConnect": false, + "enableShareableLink": false, + "scaleUnits": 2, + "skuName": "Standard" + }, + "diagnosticSettings": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ], + "dnsServers": [ + "10.0.1.6", + "10.0.1.7" + ], + "enableAzureFirewall": true, + "enableBastion": true, + "enablePeering": false, + "enableTelemetry": true, + "flowTimeoutInMinutes": 30, + "location": "", + "lock": { + "kind": "CanNotDelete", + "name": "hub1Lock" + }, + "routes": [ + { + "name": "defaultRoute", + "properties": { + "addressPrefix": "0.0.0.0/0", + "nextHopType": "Internet" + } + } + ], + "subnets": [ + { + "addressPrefix": "", + "name": "GatewaySubnet" + }, + { + "addressPrefix": "", + "name": "AzureFirewallSubnet" + }, + { + "addressPrefix": "", + "name": "AzureBastionSubnet" + } + ], + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + }, + "vnetEncryption": false, + "vnetEncryptionEnforcement": "AllowUnencrypted" + } + } + }, + "location": { + "value": "" + } + } +} +``` + +
+

+ +## Parameters + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`hubVirtualNetworks`](#parameter-hubvirtualnetworks) | object | A map of the hub virtual networks to create. | +| [`location`](#parameter-location) | string | Location for all Resources. | + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `hubVirtualNetworks` + +A map of the hub virtual networks to create. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`>Any_other_property<`](#parameter-hubvirtualnetworks>any_other_property<) | object | The hub virtual networks to create. | + +### Parameter: `hubVirtualNetworks.>Any_other_property<` + +The hub virtual networks to create. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`addressPrefixes`](#parameter-hubvirtualnetworks>any_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyAny_other_property<.addressPrefixes` + +The address prefixes for the virtual network. + +- Required: Yes +- Type: array + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings` + +The Azure Firewall config. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`additionalPublicIpConfigurations`](#parameter-hubvirtualnetworks>any_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyAny_other_property<.azureFirewallSettings.additionalPublicIpConfigurations` + +Additional public IP configurations. + +- Required: No +- Type: array + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.applicationRuleCollections` + +Application rule collections. + +- Required: No +- Type: array + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.azureSkuTier` + +Azure Firewall SKU. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.diagnosticSettings` + +Diagnostic settings. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-hubvirtualnetworks>any_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyAny_other_property<.azureFirewallSettings.diagnosticSettings.eventHubAuthorizationRuleResourceId` + +Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.diagnosticSettings.eventHubName` + +Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.value. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.diagnosticSettings.logCategoriesAndGroups` + +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-hubvirtualnetworks>any_other_propertyany_other_propertyany_other_propertyAny_other_property<.azureFirewallSettings.diagnosticSettings.logCategoriesAndGroups.category` + +Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.diagnosticSettings.logCategoriesAndGroups.categoryGroup` + +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.diagnosticSettings.logCategoriesAndGroups.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.diagnosticSettings.metricCategories` + +The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-hubvirtualnetworks>any_other_propertyany_other_propertyAny_other_property<.azureFirewallSettings.diagnosticSettings.metricCategories.category` + +Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. + +- Required: Yes +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.diagnosticSettings.metricCategories.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.diagnosticSettings.name` + +The name of diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.diagnosticSettings.storageAccountResourceId` + +Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.value. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.diagnosticSettings.workspaceResourceId` + +Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.value. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.firewallPolicyId` + +Firewall policy ID. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.hubIpAddresses` + +Hub IP addresses. + +- Required: No +- Type: object + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.location` + +The location of the virtual network. Defaults to the location of the resource group. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.lock` + +Lock settings. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-hubvirtualnetworks>any_other_propertyany_other_propertyAny_other_property<.azureFirewallSettings.lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.managementIPAddressObject` + +Management IP address configuration. + +- Required: No +- Type: object + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.managementIPResourceID` + +Management IP resource ID. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.natRuleCollections` + +NAT rule collections. + +- Required: No +- Type: array + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.networkRuleCollections` + +Network rule collections. + +- Required: No +- Type: array + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.publicIPAddressObject` + +Public IP address object. + +- Required: No +- Type: object + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.publicIPResourceID` + +Public IP resource ID. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.roleAssignments` + +Role assignments. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-hubvirtualnetworks>any_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyAny_other_property<.azureFirewallSettings.roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.roleAssignments.condition` + +The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.tags` + +Tags of the resource. + +- Required: No +- Type: object + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.threatIntelMode` + +Threat Intel mode. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.virtualHub` + +Virtual Hub ID. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.azureFirewallSettings.zones` + +Zones. + +- Required: No +- Type: array + +### Parameter: `hubVirtualNetworks.>Any_other_property<.bastionHost` + +The Azure Bastion config. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`disableCopyPaste`](#parameter-hubvirtualnetworks>any_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyAny_other_property<.bastionHost.disableCopyPaste` + +Enable/Disable copy/paste functionality. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.bastionHost.enableFileCopy` + +Enable/Disable file copy functionality. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.bastionHost.enableIpConnect` + +Enable/Disable IP connect functionality. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.bastionHost.enableShareableLink` + +Enable/Disable shareable link functionality. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.bastionHost.scaleUnits` + +The number of scale units for the Bastion host. Defaults to 4. + +- Required: No +- Type: int + +### Parameter: `hubVirtualNetworks.>Any_other_property<.bastionHost.skuName` + +The SKU name of the Bastion host. Defaults to Standard. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.ddosProtectionPlanResourceId` + +The DDoS protection plan resource ID. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.diagnosticSettings` + +The diagnostic settings of the virtual network. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-hubvirtualnetworks>any_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyAny_other_property<.diagnosticSettings.eventHubAuthorizationRuleResourceId` + +Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.diagnosticSettings.eventHubName` + +Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.value. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `hubVirtualNetworks.>Any_other_property<.diagnosticSettings.logCategoriesAndGroups` + +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-hubvirtualnetworks>any_other_propertyany_other_propertyany_other_propertyAny_other_property<.diagnosticSettings.logCategoriesAndGroups.category` + +Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.diagnosticSettings.logCategoriesAndGroups.categoryGroup` + +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.diagnosticSettings.logCategoriesAndGroups.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.diagnosticSettings.metricCategories` + +The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-hubvirtualnetworks>any_other_propertyany_other_propertyAny_other_property<.diagnosticSettings.metricCategories.category` + +Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. + +- Required: Yes +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.diagnosticSettings.metricCategories.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.diagnosticSettings.name` + +The name of diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.diagnosticSettings.storageAccountResourceId` + +Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.value. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.diagnosticSettings.workspaceResourceId` + +Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.value. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.dnsServers` + +The DNS servers of the virtual network. + +- Required: No +- Type: array + +### Parameter: `hubVirtualNetworks.>Any_other_property<.enableAzureFirewall` + +Enable/Disable Azure Firewall for the virtual network. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.enableBastion` + +Enable/Disable Azure Bastion for the virtual network. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.enablePeering` + +Enable/Disable peering for the virtual network. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.flowTimeoutInMinutes` + +The flow timeout in minutes. + +- Required: No +- Type: int + +### Parameter: `hubVirtualNetworks.>Any_other_property<.location` + +The location of the virtual network. Defaults to the location of the resource group. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.lock` + +The lock settings of the virtual network. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-hubvirtualnetworks>any_other_propertyany_other_propertyAny_other_property<.lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `hubVirtualNetworks.>Any_other_property<.lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.peeringSettings` + +The peerings of the virtual network. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowForwardedTraffic`](#parameter-hubvirtualnetworks>any_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyAny_other_property<.peeringSettings.allowForwardedTraffic` + +Allow forwarded traffic. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.peeringSettings.allowGatewayTransit` + +Allow gateway transit. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.peeringSettings.allowVirtualNetworkAccess` + +Allow virtual network access. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.peeringSettings.remoteVirtualNetworkName` + +Remote virtual network name. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.peeringSettings.useRemoteGateways` + +Use remote gateways. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.roleAssignments` + +The role assignments to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-hubvirtualnetworks>any_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyany_other_propertyAny_other_property<.roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.roleAssignments.condition` + +The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `hubVirtualNetworks.>Any_other_property<.roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + +### Parameter: `hubVirtualNetworks.>Any_other_property<.roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `hubVirtualNetworks.>Any_other_property<.routes` + +Routes to add to the virtual network route table. + +- Required: No +- Type: array + +### Parameter: `hubVirtualNetworks.>Any_other_property<.subnets` + +The subnets of the virtual network. + +- Required: No +- Type: array + +### Parameter: `hubVirtualNetworks.>Any_other_property<.tags` + +The tags of the virtual network. + +- Required: No +- Type: object + +### Parameter: `hubVirtualNetworks.>Any_other_property<.vnetEncryption` + +Enable/Disable VNet encryption. + +- Required: No +- Type: bool + +### Parameter: `hubVirtualNetworks.>Any_other_property<.vnetEncryptionEnforcement` + +The VNet encryption enforcement settings of the virtual network. + +- Required: No +- Type: string + +### Parameter: `location` + +Location for all Resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `hubAzureFirewalls` | array | Array of hub Azure Firewall resources. | +| `hubBastions` | array | Array of hub bastion resources. | +| `hubVirtualNetworks` | array | Array of hub virtual network resources. | +| `hubVirtualNetworkSubnets` | array | The subnets of the hub virtual network. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `br/public:avm/res/network/azure-firewall:0.5.0` | Remote reference | +| `br/public:avm/res/network/bastion-host:0.4.0` | Remote reference | +| `br/public:avm/res/network/route-table:0.4.0` | Remote reference | +| `br/public:avm/res/network/virtual-network:0.4.0` | Remote reference | + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/ptn/network/hub-networking/main.bicep b/avm/ptn/network/hub-networking/main.bicep new file mode 100644 index 0000000000..8406fc1cc1 --- /dev/null +++ b/avm/ptn/network/hub-networking/main.bicep @@ -0,0 +1,515 @@ +metadata name = 'Hub Networking' +metadata description = 'This module is designed to simplify the creation of multi-region hub networks in Azure. It will create a number of virtual networks and subnets, and optionally peer them together in a mesh topology with routing.' +metadata owner = 'Azure/module-maintainers' + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +// +// Add your parameters here +// + +@description('Optional. A map of the hub virtual networks to create.') +param hubVirtualNetworks hubVirtualNetworkType + +// +// Add your variables here +var hubVirtualNetworkPeerings = [for (hub, index) in items(hubVirtualNetworks ?? {}): hub.value.?peeringSettings ?? []] + +// ============== // +// Resources // +// ============== // + +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { + name: '46d3xbcp.ptn.network-hubnetworking.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } +} + +// Create hub virtual networks +module hubVirtualNetwork 'br/public:avm/res/network/virtual-network:0.4.0' = [ + for (hub, index) in items(hubVirtualNetworks ?? {}): { + name: '${uniqueString(deployment().name, location)}-${hub.key}-nvn' + params: { + // Required parameters + name: hub.key + addressPrefixes: hub.value.addressPrefixes + // Non-required parameters + ddosProtectionPlanResourceId: hub.value.?ddosProtectionPlanResourceId ?? '' + diagnosticSettings: hub.value.?diagnosticSettings ?? [] + dnsServers: hub.value.?dnsServers ?? [] + enableTelemetry: hub.value.?enableTelemetry ?? true + flowTimeoutInMinutes: hub.value.?flowTimeoutInMinutes ?? 0 + location: hub.value.?location ?? '' + lock: hub.value.?lock ?? {} + roleAssignments: hub.value.?roleAssignments ?? [] + subnets: hub.value.?subnets ?? [] + tags: hub.value.?tags ?? {} + vnetEncryption: hub.value.?vnetEncryption ?? false + vnetEncryptionEnforcement: hub.value.?vnetEncryptionEnforcement ?? '' + } + } +] + +// Create hub virtual network peerings +module hubVirtualNetworkPeer_remote 'modules/vnets.bicep' = [ + for (peer, index) in flatten(hubVirtualNetworkPeerings): { + name: '${uniqueString(deployment().name, location)}-${peer.remoteVirtualNetworkName}-nvnp' + params: { + name: peer.remoteVirtualNetworkName + } + dependsOn: hubVirtualNetwork + } +] + +// Create hub virtual network peerings +// resource hubVirtualNetworkPeer_remote 'Microsoft.Network/virtualNetworks@2023-11-01' existing = [ +// for (peer, index) in flatten(hubVirtualNetworkPeerings): { +// name: peer.remoteVirtualNetworkName +// } +// ] + +resource hubVirtualNetworkPeer_local 'Microsoft.Network/virtualNetworks@2024-01-01' existing = [ + for (hub, index) in items(hubVirtualNetworks ?? {}): if (hub.value.enablePeering) { + name: hub.key + } +] + +resource hubVirtualNetworkPeering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2024-01-01' = [ + for (peer, index) in (flatten(hubVirtualNetworkPeerings) ?? []): { + name: '${hubVirtualNetworkPeer_local[index].name}/${hubVirtualNetworkPeer_local[index].name}-to-${peer.remoteVirtualNetworkName}-peering' + properties: { + allowForwardedTraffic: peer.allowForwardedTraffic ?? false + allowGatewayTransit: peer.allowGatewayTransit ?? false + allowVirtualNetworkAccess: peer.allowVirtualNetworkAccess ?? true + useRemoteGateways: peer.useRemoteGateways ?? false + remoteVirtualNetwork: { + id: hubVirtualNetworkPeer_remote[index].outputs.resourceId + } + } + dependsOn: hubVirtualNetwork + } +] + +// Create hub virtual network route tables +module hubRouteTable 'br/public:avm/res/network/route-table:0.4.0' = [ + for (hub, index) in items(hubVirtualNetworks ?? {}): { + name: '${uniqueString(deployment().name, location)}-${hub.key}-nrt' + params: { + name: hub.key + location: hub.value.?location ?? location + disableBgpRoutePropagation: true + enableTelemetry: hub.value.?enableTelemetry ?? true + roleAssignments: hub.value.?roleAssignments ?? [] + routes: hub.value.?routes ?? [] + tags: hub.value.?tags ?? {} + } + dependsOn: hubVirtualNetwork + } +] + +// Create hub virtual network route table route +resource hubRoute 'Microsoft.Network/routeTables/routes@2024-01-01' = [ + for (peer, index) in (flatten(hubVirtualNetworkPeerings) ?? []): { + name: '${hubVirtualNetworkPeer_local[index].name}/${hubVirtualNetworkPeer_local[index].name}-to-${peer.remoteVirtualNetworkName}-route' + properties: { + addressPrefix: hubVirtualNetworkPeer_remote[index].outputs.addressPrefix + nextHopType: 'VirtualAppliance' + nextHopIpAddress: hubAzureFirewall[index].outputs.privateIp + } + dependsOn: hubVirtualNetworkPeering + } +] + +// Create Bastion host if enabled +// AzureBastionSubnet is required to deploy Bastion service. This subnet must exist in the parsubnets array if you enable Bastion Service. +// There is a minimum subnet requirement of /27 prefix. +module hubBastion 'br/public:avm/res/network/bastion-host:0.4.0' = [ + for (hub, index) in items(hubVirtualNetworks ?? {}): if (hub.value.enableBastion) { + name: '${uniqueString(deployment().name, location)}-${hub.key}-nbh' + params: { + // Required parameters + name: hub.key + virtualNetworkResourceId: hubVirtualNetwork[index].outputs.resourceId + // Non-required parameters + diagnosticSettings: hub.value.?diagnosticSettings ?? [] + disableCopyPaste: hub.value.?bastionHost.?disableCopyPaste ?? true + enableFileCopy: hub.value.?bastionHost.?enableFileCopy ?? false + enableIpConnect: hub.value.?bastionHost.?enableIpConnect ?? false + enableShareableLink: hub.value.?bastionHost.?enableShareableLink ?? false + location: hub.value.?location ?? location + enableTelemetry: hub.value.?enableTelemetry ?? true + roleAssignments: hub.value.?roleAssignments ?? [] + scaleUnits: hub.value.?bastionHost.?scaleUnits ?? 4 + skuName: hub.value.?bastionHost.?skuName ?? 'Standard' + tags: hub.value.?tags ?? {} + } + dependsOn: hubVirtualNetwork + } +] + +// Create Azure Firewall if enabled +// AzureFirewallSubnet is required to deploy Azure Firewall service. This subnet must exist in the subnets array if you enable Azure Firewall. +module hubAzureFirewall 'br/public:avm/res/network/azure-firewall:0.5.0' = [ + for (hub, index) in items(hubVirtualNetworks ?? {}): if (hub.value.enableAzureFirewall) { + name: '${uniqueString(deployment().name, location)}-${hub.key}-naf' + params: { + // Required parameters + name: hub.key + // Conditional parameters + hubIPAddresses: hub.value.?azureFirewallSettings.?hubIpAddresses ?? {} + virtualHubId: hub.value.?azureFirewallSettings.?virtualHub ?? '' + virtualNetworkResourceId: hubVirtualNetwork[index].outputs.resourceId ?? '' + // Non-required parameters + additionalPublicIpConfigurations: hub.value.?azureFirewallSettings.?additionalPublicIpConfigurations ?? [] + applicationRuleCollections: hub.value.?azureFirewallSettings.?applicationRuleCollections ?? [] + azureSkuTier: hub.value.?azureFirewallSettings.?azureSkuTier ?? {} + diagnosticSettings: hub.value.?diagnosticSettings ?? [] + enableTelemetry: hub.value.?enableTelemetry ?? true + firewallPolicyId: hub.value.?azureFirewallSettings.?firewallPolicyId ?? '' + location: hub.value.?location ?? location + lock: hub.value.?lock ?? {} + managementIPAddressObject: hub.value.?azureFirewallSettings.?managementIPAddressObject ?? {} + managementIPResourceID: hub.value.?azureFirewallSettings.?managementIPResourceID ?? '' + natRuleCollections: hub.value.?azureFirewallSettings.?natRuleCollections ?? [] + networkRuleCollections: hub.value.?azureFirewallSettings.?networkRuleCollections ?? [] + publicIPAddressObject: hub.value.?azureFirewallSettings.?publicIPAddressObject ?? {} + publicIPResourceID: hub.value.?azureFirewallSettings.?publicIPResourceID ?? '' + roleAssignments: hub.value.?roleAssignments ?? [] + tags: hub.value.?tags ?? {} + threatIntelMode: hub.value.?azureFirewallSettings.?threatIntelMode ?? '' + zones: hub.value.?azureFirewallSettings.?zones ?? [] + } + dependsOn: hubVirtualNetwork + } +] + +module hubAzureFirewallSubnet 'modules/getSubnet.bicep' = [ + for (hub, index) in items(hubVirtualNetworks ?? {}): if (hub.value.enableAzureFirewall) { + name: '${uniqueString(deployment().name, location)}-${hub.key}-nafs' + params: { + subnetName: 'AzureFirewallSubnet' + virtualNetworkName: hub.key + } + dependsOn: [hubVirtualNetwork] + } +] + +@batchSize(1) +module hubAzureFirewallSubnetAssociation 'modules/subnets.bicep' = [ + for (hub, index) in items(hubVirtualNetworks ?? {}): if (hub.value.enableAzureFirewall) { + name: '${uniqueString(deployment().name, location)}-${hub.key}-nafsa' + params: { + name: 'AzureFirewallSubnet' + virtualNetworkName: hub.key + addressPrefix: hubAzureFirewallSubnet[index].outputs.addressPrefix + routeTableResourceId: hubRouteTable[index].outputs.resourceId + } + dependsOn: [hubAzureFirewallSubnet, hubAzureFirewall, hubVirtualNetwork] + } +] + +// +// Add your resources here +// + +// ============ // +// Outputs // +// ============ // + +@description('Array of hub virtual network resources.') +output hubVirtualNetworks object[] = [ + for (hub, index) in items(hubVirtualNetworks ?? {}): { + resourceGroupName: hubVirtualNetwork[index].outputs.resourceGroupName + location: hubVirtualNetwork[index].outputs.location + name: hubVirtualNetwork[index].outputs.name + resourceId: hubVirtualNetwork[index].outputs.resourceId + } +] + +@description('Array of hub bastion resources.') +output hubBastions object[] = [ + for (hub, index) in items(hubVirtualNetworks ?? {}): { + resourceGroupName: hubBastion[index].outputs.resourceGroupName + location: hubBastion[index].outputs.location + name: hubBastion[index].outputs.name + resourceId: hubBastion[index].outputs.resourceId + } +] + +@description('Array of hub Azure Firewall resources.') +output hubAzureFirewalls object[] = [ + for (hub, index) in items(hubVirtualNetworks ?? {}): { + resourceGroupName: hubAzureFirewall[index].outputs.resourceGroupName + location: hubAzureFirewall[index].outputs.location + name: hubAzureFirewall[index].outputs.name + resourceId: hubAzureFirewall[index].outputs.resourceId + } +] + +@description('The subnets of the hub virtual network.') +output hubVirtualNetworkSubnets array = [ + for (hub, index) in items(hubVirtualNetworks ?? {}): hubVirtualNetwork[index].outputs.subnetNames +] + +// ================ // +// Definitions // +// ================ // +// +// Add your User-defined-types here, if any +// + +type lockType = { + @description('Optional. Specify the name of lock.') + name: string? + + @description('Optional. Specify the type of lock.') + kind: ('CanNotDelete' | 'ReadOnly' | 'None')? +}? + +type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') + roleDefinitionIdOrName: string + + @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') + principalId: string + + @description('Optional. The principal type of the assigned principal ID.') + principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')? + + @description('Optional. The description of the role assignment.') + description: string? + + @description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') + condition: string? + + @description('Optional. Version of the condition.') + conditionVersion: '2.0'? + + @description('Optional. The Resource Id of the delegated managed identity resource.') + delegatedManagedIdentityResourceId: string? +}[]? + +type diagnosticSettingType = { + @description('Optional. The name of diagnostic setting.') + name: string? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection.') + logCategoriesAndGroups: { + @description('Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.') + category: string? + + @description('Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs.') + categoryGroup: string? + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @description('Optional. The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection.') + metricCategories: { + @description('Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics.') + category: string + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @description('Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type.') + logAnalyticsDestinationType: ('Dedicated' | 'AzureDiagnostics')? + + @description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.value.') + workspaceResourceId: string? + + @description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.value.') + storageAccountResourceId: string? + + @description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') + eventHubAuthorizationRuleResourceId: string? + + @description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.value.') + eventHubName: string? + + @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') + marketplacePartnerResourceId: string? +}[]? + +type hubVirtualNetworkType = { + @description('Required. The hub virtual networks to create.') + *: { + @description('Required. The address prefixes for the virtual network.') + addressPrefixes: array + + @description('Optional. The Azure Firewall config.') + azureFirewallSettings: azureFirewallType? + + @description('Optional. The Azure Bastion config.') + bastionHost: { + @description('Optional. Enable/Disable copy/paste functionality.') + disableCopyPaste: bool? + + @description('Optional. Enable/Disable file copy functionality.') + enableFileCopy: bool? + + @description('Optional. Enable/Disable IP connect functionality.') + enableIpConnect: bool? + + @description('Optional. Enable/Disable shareable link functionality.') + enableShareableLink: bool? + + @description('Optional. The number of scale units for the Bastion host. Defaults to 4.') + scaleUnits: int? + + @description('Optional. The SKU name of the Bastion host. Defaults to Standard.') + skuName: string? + }? + + @description('Optional. Enable/Disable usage telemetry for module.') + enableTelemetry: bool? + + @description('Optional. Enable/Disable Azure Bastion for the virtual network.') + enableBastion: bool? + + @description('Optional. Enable/Disable Azure Firewall for the virtual network.') + enableAzureFirewall: bool? + + @description('Optional. The location of the virtual network. Defaults to the location of the resource group.') + location: string? + + @description('Optional. The lock settings of the virtual network.') + lock: lockType? + + @description('Optional. The diagnostic settings of the virtual network.') + diagnosticSettings: diagnosticSettingType? + + @description('Optional. The DDoS protection plan resource ID.') + ddosProtectionPlanResourceId: string? + + @description('Optional. The DNS servers of the virtual network.') + dnsServers: array? + + @description('Optional. The flow timeout in minutes.') + flowTimeoutInMinutes: int? + + @description('Optional. Enable/Disable peering for the virtual network.') + enablePeering: bool? + + @description('Optional. The peerings of the virtual network.') + peeringSettings: peeringSettingsType? + + @description('Optional. The role assignments to create.') + roleAssignments: roleAssignmentType? + + @description('Optional. Routes to add to the virtual network route table.') + routes: array? + + @description('Optional. The subnets of the virtual network.') + subnets: array? + + @description('Optional. The tags of the virtual network.') + tags: object? + + @description('Optional. Enable/Disable VNet encryption.') + vnetEncryption: bool? + + @description('Optional. The VNet encryption enforcement settings of the virtual network.') + vnetEncryptionEnforcement: string? + } +}? + +type peeringSettingsType = { + @description('Optional. Allow forwarded traffic.') + allowForwardedTraffic: bool? + + @description('Optional. Allow gateway transit.') + allowGatewayTransit: bool? + + @description('Optional. Allow virtual network access.') + allowVirtualNetworkAccess: bool? + + @description('Optional. Use remote gateways.') + useRemoteGateways: bool? + + @description('Optional. Remote virtual network name.') + remoteVirtualNetworkName: string? +}[]? + +type azureFirewallType = { + @description('Optional. Hub IP addresses.') + hubIpAddresses: object? + + @description('Optional. Virtual Hub ID.') + virtualHub: string? + + @description('Optional. Additional public IP configurations.') + additionalPublicIpConfigurations: array? + + @description('Optional. Application rule collections.') + applicationRuleCollections: array? + + @description('Optional. Azure Firewall SKU.') + azureSkuTier: string? + + @description('Optional. Diagnostic settings.') + diagnosticSettings: diagnosticSettingType? + + @description('Optional. Enable/Disable usage telemetry for module.') + enableTelemetry: bool? + + @description('Optional. Firewall policy ID.') + firewallPolicyId: string? + + @description('Optional. The location of the virtual network. Defaults to the location of the resource group.') + location: string? + + @description('Optional. Lock settings.') + lock: lockType? + + @description('Optional. Management IP address configuration.') + managementIPAddressObject: object? + + @description('Optional. Management IP resource ID.') + managementIPResourceID: string? + + @description('Optional. NAT rule collections.') + natRuleCollections: array? + + @description('Optional. Network rule collections.') + networkRuleCollections: array? + + @description('Optional. Public IP address object.') + publicIPAddressObject: object? + + @description('Optional. Public IP resource ID.') + publicIPResourceID: string? + + @description('Optional. Role assignments.') + roleAssignments: roleAssignmentType? + + @description('Optional. Tags of the resource.') + tags: object? + + @description('Optional. Threat Intel mode.') + threatIntelMode: string? + + @description('Optional. Zones.') + zones: int[]? +}? diff --git a/avm/ptn/network/hub-networking/main.json b/avm/ptn/network/hub-networking/main.json new file mode 100644 index 0000000000..b2149b348c --- /dev/null +++ b/avm/ptn/network/hub-networking/main.json @@ -0,0 +1,6899 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "4469274513824919483" + }, + "name": "Hub Networking", + "description": "This module is designed to simplify the creation of multi-region hub networks in Azure. It will create a number of virtual networks and subnets, and optionally peer them together in a mesh topology with routing.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.value." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.value." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.value." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + }, + "hubVirtualNetworkType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "type": "object", + "properties": { + "addressPrefixes": { + "type": "array", + "metadata": { + "description": "Required. The address prefixes for the virtual network." + } + }, + "azureFirewallSettings": { + "$ref": "#/definitions/azureFirewallType", + "nullable": true, + "metadata": { + "description": "Optional. The Azure Firewall config." + } + }, + "bastionHost": { + "type": "object", + "properties": { + "disableCopyPaste": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable copy/paste functionality." + } + }, + "enableFileCopy": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable file copy functionality." + } + }, + "enableIpConnect": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable IP connect functionality." + } + }, + "enableShareableLink": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable shareable link functionality." + } + }, + "scaleUnits": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The number of scale units for the Bastion host. Defaults to 4." + } + }, + "skuName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The SKU name of the Bastion host. Defaults to Standard." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The Azure Bastion config." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "enableBastion": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable Azure Bastion for the virtual network." + } + }, + "enableAzureFirewall": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable Azure Firewall for the virtual network." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location of the virtual network. Defaults to the location of the resource group." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. The lock settings of the virtual network." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "nullable": true, + "metadata": { + "description": "Optional. The diagnostic settings of the virtual network." + } + }, + "ddosProtectionPlanResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan resource ID." + } + }, + "dnsServers": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The DNS servers of the virtual network." + } + }, + "flowTimeoutInMinutes": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The flow timeout in minutes." + } + }, + "enablePeering": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable peering for the virtual network." + } + }, + "peeringSettings": { + "$ref": "#/definitions/peeringSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The peerings of the virtual network." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "nullable": true, + "metadata": { + "description": "Optional. The role assignments to create." + } + }, + "routes": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Routes to add to the virtual network route table." + } + }, + "subnets": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The subnets of the virtual network." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. The tags of the virtual network." + } + }, + "vnetEncryption": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable VNet encryption." + } + }, + "vnetEncryptionEnforcement": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The VNet encryption enforcement settings of the virtual network." + } + } + }, + "metadata": { + "description": "Required. The hub virtual networks to create." + } + }, + "nullable": true + }, + "peeringSettingsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "allowForwardedTraffic": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Allow forwarded traffic." + } + }, + "allowGatewayTransit": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Allow gateway transit." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Allow virtual network access." + } + }, + "useRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Use remote gateways." + } + }, + "remoteVirtualNetworkName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Remote virtual network name." + } + } + } + }, + "nullable": true + }, + "azureFirewallType": { + "type": "object", + "properties": { + "hubIpAddresses": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Hub IP addresses." + } + }, + "virtualHub": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Virtual Hub ID." + } + }, + "additionalPublicIpConfigurations": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Additional public IP configurations." + } + }, + "applicationRuleCollections": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Application rule collections." + } + }, + "azureSkuTier": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Azure Firewall SKU." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "nullable": true, + "metadata": { + "description": "Optional. Diagnostic settings." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "firewallPolicyId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Firewall policy ID." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location of the virtual network. Defaults to the location of the resource group." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "nullable": true, + "metadata": { + "description": "Optional. Lock settings." + } + }, + "managementIPAddressObject": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Management IP address configuration." + } + }, + "managementIPResourceID": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Management IP resource ID." + } + }, + "natRuleCollections": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. NAT rule collections." + } + }, + "networkRuleCollections": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Network rule collections." + } + }, + "publicIPAddressObject": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Public IP address object." + } + }, + "publicIPResourceID": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Public IP resource ID." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "nullable": true, + "metadata": { + "description": "Optional. Role assignments." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "threatIntelMode": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Threat Intel mode." + } + }, + "zones": { + "type": "array", + "items": { + "type": "int" + }, + "nullable": true, + "metadata": { + "description": "Optional. Zones." + } + } + }, + "nullable": true + } + }, + "parameters": { + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "hubVirtualNetworks": { + "$ref": "#/definitions/hubVirtualNetworkType", + "metadata": { + "description": "Optional. A map of the hub virtual networks to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "hubVirtualNetworkPeerings", + "count": "[length(items(coalesce(parameters('hubVirtualNetworks'), createObject())))]", + "input": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex('hubVirtualNetworkPeerings')].value, 'peeringSettings'), createArray())]" + } + ] + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.ptn.network-hubnetworking.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "hubVirtualNetworkPeer_local": { + "copy": { + "name": "hubVirtualNetworkPeer_local", + "count": "[length(items(coalesce(parameters('hubVirtualNetworks'), createObject())))]" + }, + "condition": "[items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value.enablePeering]", + "existing": true, + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2024-01-01", + "name": "[items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key]" + }, + "hubVirtualNetworkPeering": { + "copy": { + "name": "hubVirtualNetworkPeering", + "count": "[length(coalesce(flatten(variables('hubVirtualNetworkPeerings')), createArray()))]" + }, + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}-to-{2}-peering', items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key, items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key, coalesce(flatten(variables('hubVirtualNetworkPeerings')), createArray())[copyIndex()].remoteVirtualNetworkName)]", + "properties": { + "allowForwardedTraffic": "[coalesce(coalesce(flatten(variables('hubVirtualNetworkPeerings')), createArray())[copyIndex()].allowForwardedTraffic, false())]", + "allowGatewayTransit": "[coalesce(coalesce(flatten(variables('hubVirtualNetworkPeerings')), createArray())[copyIndex()].allowGatewayTransit, false())]", + "allowVirtualNetworkAccess": "[coalesce(coalesce(flatten(variables('hubVirtualNetworkPeerings')), createArray())[copyIndex()].allowVirtualNetworkAccess, true())]", + "useRemoteGateways": "[coalesce(coalesce(flatten(variables('hubVirtualNetworkPeerings')), createArray())[copyIndex()].useRemoteGateways, false())]", + "remoteVirtualNetwork": { + "id": "[reference(format('hubVirtualNetworkPeer_remote[{0}]', copyIndex())).outputs.resourceId.value]" + } + }, + "dependsOn": [ + "hubVirtualNetwork", + "[format('hubVirtualNetworkPeer_local[{0}]', copyIndex())]", + "[format('hubVirtualNetworkPeer_local[{0}]', copyIndex())]", + "[format('hubVirtualNetworkPeer_remote[{0}]', copyIndex())]" + ] + }, + "hubRoute": { + "copy": { + "name": "hubRoute", + "count": "[length(coalesce(flatten(variables('hubVirtualNetworkPeerings')), createArray()))]" + }, + "type": "Microsoft.Network/routeTables/routes", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}-to-{2}-route', items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key, items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key, coalesce(flatten(variables('hubVirtualNetworkPeerings')), createArray())[copyIndex()].remoteVirtualNetworkName)]", + "properties": { + "addressPrefix": "[reference(format('hubVirtualNetworkPeer_remote[{0}]', copyIndex())).outputs.addressPrefix.value]", + "nextHopType": "VirtualAppliance", + "nextHopIpAddress": "[reference(format('hubAzureFirewall[{0}]', copyIndex())).outputs.privateIp.value]" + }, + "dependsOn": [ + "[format('hubAzureFirewall[{0}]', copyIndex())]", + "[format('hubVirtualNetworkPeer_local[{0}]', copyIndex())]", + "[format('hubVirtualNetworkPeer_local[{0}]', copyIndex())]", + "[format('hubVirtualNetworkPeer_remote[{0}]', copyIndex())]", + "hubVirtualNetworkPeering" + ] + }, + "hubVirtualNetwork": { + "copy": { + "name": "hubVirtualNetwork", + "count": "[length(items(coalesce(parameters('hubVirtualNetworks'), createObject())))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}-nvn', uniqueString(deployment().name, parameters('location')), items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key]" + }, + "addressPrefixes": { + "value": "[items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value.addressPrefixes]" + }, + "ddosProtectionPlanResourceId": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'ddosProtectionPlanResourceId'), '')]" + }, + "diagnosticSettings": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'diagnosticSettings'), createArray())]" + }, + "dnsServers": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'dnsServers'), createArray())]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'enableTelemetry'), true())]" + }, + "flowTimeoutInMinutes": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'flowTimeoutInMinutes'), 0)]" + }, + "location": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'location'), '')]" + }, + "lock": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'lock'), createObject())]" + }, + "roleAssignments": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'roleAssignments'), createArray())]" + }, + "subnets": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'subnets'), createArray())]" + }, + "tags": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'tags'), createObject())]" + }, + "vnetEncryption": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'vnetEncryption'), false())]" + }, + "vnetEncryptionEnforcement": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'vnetEncryptionEnforcement'), '')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "15949466154563447171" + }, + "name": "Virtual Networks", + "description": "This module deploys a Virtual Network (vNet).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + }, + "peeringType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be peer-localVnetName-remoteVnetName." + } + }, + "remoteVirtualNetworkResourceId": { + "type": "string", + "metadata": { + "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." + } + }, + "allowForwardedTraffic": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "allowGatewayTransit": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Do not verify the provisioning state of the remote gateway. Default is true." + } + }, + "useRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "remotePeeringEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Deploy the outbound and the inbound peering." + } + }, + "remotePeeringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the VNET Peering resource in the remove Virtual Network. If not provided, default value will be peer-remoteVnetName-localVnetName." + } + }, + "remotePeeringAllowForwardedTraffic": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "remotePeeringAllowGatewayTransit": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "remotePeeringAllowVirtualNetworkAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "remotePeeringDoNotVerifyRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Do not verify the provisioning state of the remote gateway. Default is true." + } + }, + "remotePeeringUseRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + } + } + }, + "subnetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The Name of the subnet resource." + } + }, + "addressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." + } + }, + "applicationGatewayIPConfigurations": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application gateway IP configurations of virtual network resource." + } + }, + "delegation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The delegation to enable on the subnet." + } + }, + "natGatewayResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the network security group to assign to the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "allowedValues": [ + "", + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "allowedValues": [ + "", + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. enable or disable apply network policies on private link service in the subnet." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "routeTableResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "serviceEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + }, + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty." + } + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Virtual Network (vNet)." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "addressPrefixes": { + "type": "array", + "metadata": { + "description": "Required. An Array of 1 or more IP Address Prefixes for the Virtual Network." + } + }, + "virtualNetworkBgpCommunity": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The BGP community associated with the virtual network." + } + }, + "subnets": { + "type": "array", + "items": { + "$ref": "#/definitions/subnetType" + }, + "nullable": true, + "metadata": { + "description": "Optional. An Array of subnets to deploy to the Virtual Network." + } + }, + "dnsServers": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. DNS Servers associated to the Virtual Network." + } + }, + "ddosProtectionPlanResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "peerings": { + "type": "array", + "items": { + "$ref": "#/definitions/peeringType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Virtual Network Peering configurations." + } + }, + "vnetEncryption": { + "type": "bool", + "defaultValue": false, + "metadata": { + "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." + } + }, + "vnetEncryptionEnforcement": { + "type": "string", + "defaultValue": "AllowUnencrypted", + "allowedValues": [ + "AllowUnencrypted", + "DropUnencrypted" + ], + "metadata": { + "description": "Optional. If the encrypted VNet allows VM that does not support encryption. Can only be used when vnetEncryption is enabled." + } + }, + "flowTimeoutInMinutes": { + "type": "int", + "defaultValue": 0, + "maxValue": 30, + "metadata": { + "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." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "enableVmProtection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Indicates if VM protection is enabled for all the subnets in the virtual network." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-virtualnetwork.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "virtualNetwork": { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2024-01-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "addressSpace": { + "addressPrefixes": "[parameters('addressPrefixes')]" + }, + "bgpCommunities": "[if(not(empty(parameters('virtualNetworkBgpCommunity'))), createObject('virtualNetworkCommunity', parameters('virtualNetworkBgpCommunity')), null())]", + "ddosProtectionPlan": "[if(not(empty(parameters('ddosProtectionPlanResourceId'))), createObject('id', parameters('ddosProtectionPlanResourceId')), null())]", + "dhcpOptions": "[if(not(empty(parameters('dnsServers'))), createObject('dnsServers', array(parameters('dnsServers'))), null())]", + "enableDdosProtection": "[not(empty(parameters('ddosProtectionPlanResourceId')))]", + "encryption": "[if(equals(parameters('vnetEncryption'), true()), createObject('enabled', parameters('vnetEncryption'), 'enforcement', parameters('vnetEncryptionEnforcement')), null())]", + "flowTimeoutInMinutes": "[if(not(equals(parameters('flowTimeoutInMinutes'), 0)), parameters('flowTimeoutInMinutes'), null())]", + "enableVmProtection": "[parameters('enableVmProtection')]" + } + }, + "virtualNetwork_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_diagnosticSettings": { + "copy": { + "name": "virtualNetwork_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_roleAssignments": { + "copy": { + "name": "virtualNetwork_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_subnets": { + "copy": { + "name": "virtualNetwork_subnets", + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-subnet-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualNetworkName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('subnets'), createArray())[copyIndex()].name]" + }, + "addressPrefix": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'addressPrefix')]" + }, + "addressPrefixes": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'addressPrefixes')]" + }, + "applicationGatewayIPConfigurations": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'applicationGatewayIPConfigurations')]" + }, + "delegation": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'delegation')]" + }, + "natGatewayResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'natGatewayResourceId')]" + }, + "networkSecurityGroupResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'networkSecurityGroupResourceId')]" + }, + "privateEndpointNetworkPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'privateEndpointNetworkPolicies')]" + }, + "privateLinkServiceNetworkPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'privateLinkServiceNetworkPolicies')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "routeTableResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'routeTableResourceId')]" + }, + "serviceEndpointPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'serviceEndpointPolicies')]" + }, + "serviceEndpoints": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'serviceEndpoints')]" + }, + "defaultOutboundAccess": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'defaultOutboundAccess')]" + }, + "sharingScope": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'sharingScope')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5699372618313647761" + }, + "name": "Virtual Network Subnets", + "description": "This module deploys a Virtual Network Subnet.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Requird. The Name of the subnet resource." + } + }, + "virtualNetworkName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual network. Required if the template is used in a standalone deployment." + } + }, + "addressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the network security group to assign to the subnet." + } + }, + "routeTableResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "delegation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The delegation to enable on the subnet." + } + }, + "natGatewayResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "Disabled", + "Enabled", + "" + ], + "metadata": { + "description": "Optional. Enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "Disabled", + "Enabled", + "" + ], + "metadata": { + "description": "Optional. Enable or disable apply network policies on private link service in the subnet." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." + } + }, + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + }, + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty." + } + }, + "applicationGatewayIPConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application gateway IP configurations of virtual network resource." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "virtualNetwork": { + "existing": true, + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2024-01-01", + "name": "[parameters('virtualNetworkName')]" + }, + "subnet": { + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "properties": { + "copy": [ + { + "name": "serviceEndpoints", + "count": "[length(parameters('serviceEndpoints'))]", + "input": { + "service": "[parameters('serviceEndpoints')[copyIndex('serviceEndpoints')]]" + } + } + ], + "addressPrefix": "[parameters('addressPrefix')]", + "addressPrefixes": "[parameters('addressPrefixes')]", + "networkSecurityGroup": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('id', parameters('networkSecurityGroupResourceId')), null())]", + "routeTable": "[if(not(empty(parameters('routeTableResourceId'))), createObject('id', parameters('routeTableResourceId')), null())]", + "natGateway": "[if(not(empty(parameters('natGatewayResourceId'))), createObject('id', parameters('natGatewayResourceId')), null())]", + "delegations": "[if(not(empty(parameters('delegation'))), createArray(createObject('name', parameters('delegation'), 'properties', createObject('serviceName', parameters('delegation')))), createArray())]", + "privateEndpointNetworkPolicies": "[if(not(empty(parameters('privateEndpointNetworkPolicies'))), parameters('privateEndpointNetworkPolicies'), null())]", + "privateLinkServiceNetworkPolicies": "[if(not(empty(parameters('privateLinkServiceNetworkPolicies'))), parameters('privateLinkServiceNetworkPolicies'), null())]", + "applicationGatewayIPConfigurations": "[parameters('applicationGatewayIPConfigurations')]", + "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]", + "defaultOutboundAccess": "[parameters('defaultOutboundAccess')]", + "sharingScope": "[parameters('sharingScope')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "subnet_roleAssignments": { + "copy": { + "name": "subnet_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}/subnets/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "subnet" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name'))]" + }, + "addressPrefix": { + "type": "string", + "metadata": { + "description": "The address prefix for the subnet." + }, + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefix'), '')]" + }, + "addressPrefixes": { + "type": "array", + "metadata": { + "description": "List of address prefixes for the subnet." + }, + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefixes'), createArray())]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_peering_local": { + "copy": { + "name": "virtualNetwork_peering_local", + "count": "[length(coalesce(parameters('peerings'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-virtualNetworkPeering-local-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "localVnetName": { + "value": "[parameters('name')]" + }, + "remoteVirtualNetworkResourceId": { + "value": "[coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId]" + }, + "name": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'name')]" + }, + "allowForwardedTraffic": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowForwardedTraffic')]" + }, + "allowGatewayTransit": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowGatewayTransit')]" + }, + "allowVirtualNetworkAccess": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowVirtualNetworkAccess')]" + }, + "doNotVerifyRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'doNotVerifyRemoteGateways')]" + }, + "useRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'useRemoteGateways')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5206620163504251868" + }, + "name": "Virtual Network Peerings", + "description": "This module deploys a Virtual Network Peering.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + } + }, + "localVnetName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." + } + }, + "remoteVirtualNetworkResourceId": { + "type": "string", + "metadata": { + "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." + } + }, + "allowForwardedTraffic": { + "type": "bool", + "defaultValue": true, + "metadata": { + "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." + } + }, + "allowGatewayTransit": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "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." + } + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. If we need to verify the provisioning state of the remote gateway. Default is true." + } + }, + "useRemoteGateways": { + "type": "bool", + "defaultValue": false, + "metadata": { + "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." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", + "properties": { + "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", + "allowGatewayTransit": "[parameters('allowGatewayTransit')]", + "allowVirtualNetworkAccess": "[parameters('allowVirtualNetworkAccess')]", + "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", + "useRemoteGateways": "[parameters('useRemoteGateways')]", + "remoteVirtualNetwork": { + "id": "[parameters('remoteVirtualNetworkResourceId')]" + } + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/virtualNetworkPeerings', parameters('localVnetName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_peering_remote": { + "copy": { + "name": "virtualNetwork_peering_remote", + "count": "[length(coalesce(parameters('peerings'), createArray()))]" + }, + "condition": "[coalesce(tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringEnabled'), false())]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-virtualNetworkPeering-remote-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/')[2]]", + "resourceGroup": "[split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "localVnetName": { + "value": "[last(split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/'))]" + }, + "remoteVirtualNetworkResourceId": { + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" + }, + "name": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringName')]" + }, + "allowForwardedTraffic": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowForwardedTraffic')]" + }, + "allowGatewayTransit": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowGatewayTransit')]" + }, + "allowVirtualNetworkAccess": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowVirtualNetworkAccess')]" + }, + "doNotVerifyRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringDoNotVerifyRemoteGateways')]" + }, + "useRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringUseRemoteGateways')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5206620163504251868" + }, + "name": "Virtual Network Peerings", + "description": "This module deploys a Virtual Network Peering.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + } + }, + "localVnetName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." + } + }, + "remoteVirtualNetworkResourceId": { + "type": "string", + "metadata": { + "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." + } + }, + "allowForwardedTraffic": { + "type": "bool", + "defaultValue": true, + "metadata": { + "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." + } + }, + "allowGatewayTransit": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "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." + } + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. If we need to verify the provisioning state of the remote gateway. Default is true." + } + }, + "useRemoteGateways": { + "type": "bool", + "defaultValue": false, + "metadata": { + "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." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", + "properties": { + "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", + "allowGatewayTransit": "[parameters('allowGatewayTransit')]", + "allowVirtualNetworkAccess": "[parameters('allowVirtualNetworkAccess')]", + "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", + "useRemoteGateways": "[parameters('useRemoteGateways')]", + "remoteVirtualNetwork": { + "id": "[parameters('remoteVirtualNetworkResourceId')]" + } + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/virtualNetworkPeerings', parameters('localVnetName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network." + }, + "value": "[parameters('name')]" + }, + "subnetNames": { + "type": "array", + "metadata": { + "description": "The names of the deployed subnets." + }, + "copy": { + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "input": "[reference(format('virtualNetwork_subnets[{0}]', copyIndex())).outputs.name.value]" + } + }, + "subnetResourceIds": { + "type": "array", + "metadata": { + "description": "The resource IDs of the deployed subnets." + }, + "copy": { + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "input": "[reference(format('virtualNetwork_subnets[{0}]', copyIndex())).outputs.resourceId.value]" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetwork', '2024-01-01', 'full').location]" + } + } + } + } + }, + "hubVirtualNetworkPeer_remote": { + "copy": { + "name": "hubVirtualNetworkPeer_remote", + "count": "[length(flatten(variables('hubVirtualNetworkPeerings')))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}-nvnp', uniqueString(deployment().name, parameters('location')), flatten(variables('hubVirtualNetworkPeerings'))[copyIndex()].remoteVirtualNetworkName)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[flatten(variables('hubVirtualNetworkPeerings'))[copyIndex()].remoteVirtualNetworkName]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "15958982442955537466" + }, + "name": "Virtual Networks", + "description": "This module deploys a Virtual Network.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent virtual network. Required if the template is used in a standalone deployment." + } + } + }, + "resources": [], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" + }, + "addressPrefix": { + "type": "string", + "metadata": { + "description": "The address space of the virtual network." + }, + "value": "[reference(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), '2024-01-01').addressSpace.addressPrefixes[0]]" + } + } + } + }, + "dependsOn": [ + "hubVirtualNetwork" + ] + }, + "hubRouteTable": { + "copy": { + "name": "hubRouteTable", + "count": "[length(items(coalesce(parameters('hubVirtualNetworks'), createObject())))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}-nrt', uniqueString(deployment().name, parameters('location')), items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key]" + }, + "location": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'location'), parameters('location'))]" + }, + "disableBgpRoutePropagation": { + "value": true + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'enableTelemetry'), true())]" + }, + "roleAssignments": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'roleAssignments'), createArray())]" + }, + "routes": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'routes'), createArray())]" + }, + "tags": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'tags'), createObject())]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5827817137345359685" + }, + "name": "Route Tables", + "description": "This module deploys a User Defined Route Table (UDR).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "routeType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the route." + } + }, + "properties": { + "type": "object", + "properties": { + "nextHopType": { + "type": "string", + "allowedValues": [ + "Internet", + "None", + "VirtualAppliance", + "VirtualNetworkGateway", + "VnetLocal" + ], + "metadata": { + "description": "Required. The type of Azure hop the packet should be sent to." + } + }, + "addressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The destination CIDR to which the route applies." + } + }, + "hasBgpOverride": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. A value indicating whether this route overrides overlapping BGP routes regardless of LPM." + } + }, + "nextHopIpAddress": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The IP address packets should be forwarded to. Next hop values are only allowed in routes where the next hop type is VirtualAppliance." + } + } + }, + "metadata": { + "description": "Required. Properties of the route." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name given for the hub route table." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "routes": { + "$ref": "#/definitions/routeType", + "metadata": { + "description": "Optional. An array of routes to be established within the hub route table." + } + }, + "disableBgpRoutePropagation": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Switch to disable BGP route propagation." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[take(format('46d3xbcp.res.network-routetable.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4)), 64)]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "routeTable": { + "type": "Microsoft.Network/routeTables", + "apiVersion": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "routes": "[parameters('routes')]", + "disableBgpRoutePropagation": "[parameters('disableBgpRoutePropagation')]" + } + }, + "routeTable_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/routeTables/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "routeTable" + ] + }, + "routeTable_roleAssignments": { + "copy": { + "name": "routeTable_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/routeTables/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/routeTables', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "routeTable" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the route table was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the route table." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the route table." + }, + "value": "[resourceId('Microsoft.Network/routeTables', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('routeTable', '2023-04-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "hubVirtualNetwork" + ] + }, + "hubBastion": { + "copy": { + "name": "hubBastion", + "count": "[length(items(coalesce(parameters('hubVirtualNetworks'), createObject())))]" + }, + "condition": "[items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value.enableBastion]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}-nbh', uniqueString(deployment().name, parameters('location')), items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key]" + }, + "virtualNetworkResourceId": { + "value": "[reference(format('hubVirtualNetwork[{0}]', copyIndex())).outputs.resourceId.value]" + }, + "diagnosticSettings": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'diagnosticSettings'), createArray())]" + }, + "disableCopyPaste": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'bastionHost'), 'disableCopyPaste'), true())]" + }, + "enableFileCopy": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'bastionHost'), 'enableFileCopy'), false())]" + }, + "enableIpConnect": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'bastionHost'), 'enableIpConnect'), false())]" + }, + "enableShareableLink": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'bastionHost'), 'enableShareableLink'), false())]" + }, + "location": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'location'), parameters('location'))]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'enableTelemetry'), true())]" + }, + "roleAssignments": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'roleAssignments'), createArray())]" + }, + "scaleUnits": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'bastionHost'), 'scaleUnits'), 4)]" + }, + "skuName": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'bastionHost'), 'skuName'), 'Standard')]" + }, + "tags": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'tags'), createObject())]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "11368267491813619372" + }, + "name": "Bastion Hosts", + "description": "This module deploys a Bastion Host.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Azure Bastion resource." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "Required. Shared services Virtual Network resource Id." + } + }, + "bastionSubnetPublicIpResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "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." + } + }, + "publicIPAddressObject": { + "type": "object", + "defaultValue": { + "name": "[format('{0}-pip', parameters('name'))]" + }, + "metadata": { + "description": "Optional. Specifies the properties of the Public IP to create and be used by Azure Bastion, if no existing public IP was provided." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Basic", + "allowedValues": [ + "Basic", + "Standard" + ], + "metadata": { + "description": "Optional. The SKU of this Bastion Host." + } + }, + "disableCopyPaste": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Copy Paste." + } + }, + "enableFileCopy": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Choose to disable or enable File Copy." + } + }, + "enableIpConnect": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable IP Connect." + } + }, + "enableKerberos": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Kerberos authentication." + } + }, + "enableShareableLink": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Choose to disable or enable Shareable Link." + } + }, + "scaleUnits": { + "type": "int", + "defaultValue": 2, + "metadata": { + "description": "Optional. The scale units for the Bastion Host resource." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-bastionhost.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "azureBastion": { + "type": "Microsoft.Network/bastionHosts", + "apiVersion": "2022-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('skuName')]" + }, + "properties": "[union(createObject('scaleUnits', if(equals(parameters('skuName'), 'Basic'), 2, parameters('scaleUnits')), 'ipConfigurations', createArray(createObject('name', 'IpConfAzureBastionSubnet', 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureBastionSubnet', parameters('virtualNetworkResourceId')))), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('bastionSubnetPublicIpResourceId'))), parameters('bastionSubnetPublicIpResourceId'), reference('publicIPAddress').outputs.resourceId.value)))))), 'enableKerberos', parameters('enableKerberos')), if(equals(parameters('skuName'), 'Standard'), createObject('enableTunneling', equals(parameters('skuName'), 'Standard'), 'disableCopyPaste', parameters('disableCopyPaste'), 'enableFileCopy', parameters('enableFileCopy'), 'enableIpConnect', parameters('enableIpConnect'), 'enableShareableLink', parameters('enableShareableLink')), createObject()))]", + "dependsOn": [ + "publicIPAddress" + ] + }, + "azureBastion_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/bastionHosts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "azureBastion" + ] + }, + "azureBastion_diagnosticSettings": { + "copy": { + "name": "azureBastion_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/bastionHosts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "azureBastion" + ] + }, + "azureBastion_roleAssignments": { + "copy": { + "name": "azureBastion_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/bastionHosts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/bastionHosts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "azureBastion" + ] + }, + "publicIPAddress": { + "condition": "[empty(parameters('bastionSubnetPublicIpResourceId'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Bastion-PIP', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('publicIPAddressObject').name]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "lock": { + "value": "[parameters('lock')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'diagnosticSettings')]" + }, + "publicIPAddressVersion": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'publicIPAddressVersion')]" + }, + "publicIPAllocationMethod": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'publicIPAllocationMethod')]" + }, + "publicIpPrefixResourceId": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'publicIPPrefixResourceId')]" + }, + "roleAssignments": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'roleAssignments')]" + }, + "skuName": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'skuName')]" + }, + "skuTier": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'skuTier')]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('publicIPAddressObject'), 'tags'), parameters('tags'))]" + }, + "zones": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'zones')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "14450344965065009842" + }, + "name": "Public IP Addresses", + "description": "This module deploys a Public IP Address.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "dnsSettingsType": { + "type": "object", + "properties": { + "domainNameLabel": { + "type": "string", + "metadata": { + "description": "Required. 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." + } + }, + "domainNameLabelScope": { + "type": "string", + "allowedValues": [ + "", + "NoReuse", + "ResourceGroupReuse", + "SubscriptionReuse", + "TenantReuse" + ], + "metadata": { + "description": "Required. The domain name label scope. If a domain name label and a domain name label scope are specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system with a hashed value includes in FQDN." + } + }, + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "reverseFqdn": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + } + } + }, + "ddosSettingsType": { + "type": "object", + "properties": { + "ddosProtectionPlan": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the DDOS protection plan associated with the public IP address." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan associated with the public IP address." + } + }, + "protectionMode": { + "type": "string", + "allowedValues": [ + "Enabled" + ], + "metadata": { + "description": "Required. The DDoS protection policy customizations." + } + } + } + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Public IP Address." + } + }, + "publicIpPrefixResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "publicIPAllocationMethod": { + "type": "string", + "defaultValue": "Static", + "allowedValues": [ + "Dynamic", + "Static" + ], + "metadata": { + "description": "Optional. The public IP address allocation method." + } + }, + "zones": { + "type": "array", + "items": { + "type": "int" + }, + "defaultValue": [ + 1, + 2, + 3 + ], + "allowedValues": [ + 1, + 2, + 3 + ], + "metadata": { + "description": "Optional. A list of availability zones denoting the IP allocated for the resource needs to come from." + } + }, + "publicIPAddressVersion": { + "type": "string", + "defaultValue": "IPv4", + "allowedValues": [ + "IPv4", + "IPv6" + ], + "metadata": { + "description": "Optional. IP address version." + } + }, + "dnsSettings": { + "$ref": "#/definitions/dnsSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DNS settings of the public IP address." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Basic", + "Standard" + ], + "metadata": { + "description": "Optional. Name of a public IP address SKU." + } + }, + "skuTier": { + "type": "string", + "defaultValue": "Regional", + "allowedValues": [ + "Global", + "Regional" + ], + "metadata": { + "description": "Optional. Tier of a public IP address SKU." + } + }, + "ddosSettings": { + "$ref": "#/definitions/ddosSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan configuration associated with the public IP address." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "idleTimeoutInMinutes": { + "type": "int", + "defaultValue": 4, + "metadata": { + "description": "Optional. The idle timeout of the public IP address." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.5.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "publicIpAddress": { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2023-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('skuName')]", + "tier": "[parameters('skuTier')]" + }, + "zones": "[map(parameters('zones'), lambda('zone', string(lambdaVariables('zone'))))]", + "properties": { + "ddosSettings": "[parameters('ddosSettings')]", + "dnsSettings": "[parameters('dnsSettings')]", + "publicIPAddressVersion": "[parameters('publicIPAddressVersion')]", + "publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]", + "publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]", + "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]", + "ipTags": null + } + }, + "publicIpAddress_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_roleAssignments": { + "copy": { + "name": "publicIpAddress_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_diagnosticSettings": { + "copy": { + "name": "publicIpAddress_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the public IP address was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the public IP address." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the public IP address." + }, + "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('name'))]" + }, + "ipAddress": { + "type": "string", + "metadata": { + "description": "The public IP address of the public IP address resource." + }, + "value": "[coalesce(tryGet(reference('publicIpAddress'), 'ipAddress'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('publicIpAddress', '2023-09-01', 'full').location]" + } + } + } + } + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the Azure Bastion was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name the Azure Bastion." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID the Azure Bastion." + }, + "value": "[resourceId('Microsoft.Network/bastionHosts', parameters('name'))]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('azureBastion', '2022-11-01', 'full').location]" + }, + "ipConfAzureBastionSubnet": { + "type": "object", + "metadata": { + "description": "The Public IPconfiguration object for the AzureBastionSubnet." + }, + "value": "[reference('azureBastion').ipConfigurations[0]]" + } + } + } + }, + "dependsOn": [ + "hubVirtualNetwork" + ] + }, + "hubAzureFirewall": { + "copy": { + "name": "hubAzureFirewall", + "count": "[length(items(coalesce(parameters('hubVirtualNetworks'), createObject())))]" + }, + "condition": "[items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value.enableAzureFirewall]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}-naf', uniqueString(deployment().name, parameters('location')), items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key]" + }, + "hubIPAddresses": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'azureFirewallSettings'), 'hubIpAddresses'), createObject())]" + }, + "virtualHubId": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'azureFirewallSettings'), 'virtualHub'), '')]" + }, + "virtualNetworkResourceId": { + "value": "[coalesce(reference(format('hubVirtualNetwork[{0}]', copyIndex())).outputs.resourceId.value, '')]" + }, + "additionalPublicIpConfigurations": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'azureFirewallSettings'), 'additionalPublicIpConfigurations'), createArray())]" + }, + "applicationRuleCollections": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'azureFirewallSettings'), 'applicationRuleCollections'), createArray())]" + }, + "azureSkuTier": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'azureFirewallSettings'), 'azureSkuTier'), createObject())]" + }, + "diagnosticSettings": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'diagnosticSettings'), createArray())]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'enableTelemetry'), true())]" + }, + "firewallPolicyId": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'azureFirewallSettings'), 'firewallPolicyId'), '')]" + }, + "location": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'location'), parameters('location'))]" + }, + "lock": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'lock'), createObject())]" + }, + "managementIPAddressObject": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'azureFirewallSettings'), 'managementIPAddressObject'), createObject())]" + }, + "managementIPResourceID": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'azureFirewallSettings'), 'managementIPResourceID'), '')]" + }, + "natRuleCollections": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'azureFirewallSettings'), 'natRuleCollections'), createArray())]" + }, + "networkRuleCollections": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'azureFirewallSettings'), 'networkRuleCollections'), createArray())]" + }, + "publicIPAddressObject": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'azureFirewallSettings'), 'publicIPAddressObject'), createObject())]" + }, + "publicIPResourceID": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'azureFirewallSettings'), 'publicIPResourceID'), '')]" + }, + "roleAssignments": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'roleAssignments'), createArray())]" + }, + "tags": { + "value": "[coalesce(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'tags'), createObject())]" + }, + "threatIntelMode": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'azureFirewallSettings'), 'threatIntelMode'), '')]" + }, + "zones": { + "value": "[coalesce(tryGet(tryGet(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value, 'azureFirewallSettings'), 'zones'), createArray())]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "15791050653269307918" + }, + "name": "Azure Firewalls", + "description": "This module deploys an Azure Firewall.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + }, + "natRuleCollectionType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the NAT rule collection." + } + }, + "properties": { + "type": "object", + "properties": { + "action": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "Dnat", + "Snat" + ], + "metadata": { + "description": "Required. The type of action." + } + } + }, + "metadata": { + "description": "Required. The action type of a NAT rule collection." + } + }, + "priority": { + "type": "int", + "minValue": 100, + "maxValue": 65000, + "metadata": { + "description": "Required. Priority of the NAT rule collection." + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the NAT rule." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description of the rule." + } + }, + "protocols": { + "type": "array", + "allowedValues": [ + "Any", + "ICMP", + "TCP", + "UDP" + ], + "metadata": { + "description": "Required. Array of AzureFirewallNetworkRuleProtocols applicable to this NAT rule." + } + }, + "destinationAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of destination IP addresses for this rule. Supports IP ranges, prefixes, and service tags." + } + }, + "destinationPorts": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of destination ports." + } + }, + "sourceAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of source IP addresses for this rule." + } + }, + "sourceIpGroups": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of source IpGroups for this rule." + } + }, + "translatedAddress": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The translated address for this NAT rule." + } + }, + "translatedFqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The translated FQDN for this NAT rule." + } + }, + "translatedPort": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The translated port for this NAT rule." + } + } + } + }, + "metadata": { + "description": "Required. Collection of rules used by a NAT rule collection." + } + } + }, + "metadata": { + "description": "Required. Properties of the azure firewall NAT rule collection." + } + } + } + }, + "nullable": true + }, + "applicationRuleCollectionType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the application rule collection." + } + }, + "properties": { + "type": "object", + "properties": { + "action": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Required. The type of action." + } + } + }, + "metadata": { + "description": "Required. The action type of a rule collection." + } + }, + "priority": { + "type": "int", + "minValue": 100, + "maxValue": 65000, + "metadata": { + "description": "Required. Priority of the application rule collection." + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the application rule." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description of the rule." + } + }, + "protocols": { + "type": "array", + "items": { + "type": "object", + "properties": { + "port": { + "type": "int", + "nullable": true, + "maxValue": 64000, + "metadata": { + "description": "Optional. Port number for the protocol." + } + }, + "protocolType": { + "type": "string", + "allowedValues": [ + "Http", + "Https", + "Mssql" + ], + "metadata": { + "description": "Required. Protocol type." + } + } + } + }, + "metadata": { + "description": "Required. Array of ApplicationRuleProtocols." + } + }, + "fqdnTags": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of FQDN Tags for this rule." + } + }, + "targetFqdns": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of FQDNs for this rule." + } + }, + "sourceAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of source IP addresses for this rule." + } + }, + "sourceIpGroups": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of source IpGroups for this rule." + } + } + } + }, + "metadata": { + "description": "Required. Collection of rules used by a application rule collection." + } + } + }, + "metadata": { + "description": "Required. Properties of the azure firewall application rule collection." + } + } + } + }, + "nullable": true + }, + "networkRuleCollectionType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the network rule collection." + } + }, + "properties": { + "type": "object", + "properties": { + "action": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "Allow", + "Deny" + ], + "metadata": { + "description": "Required. The type of action." + } + } + }, + "metadata": { + "description": "Required. The action type of a rule collection." + } + }, + "priority": { + "type": "int", + "minValue": 100, + "maxValue": 65000, + "metadata": { + "description": "Required. Priority of the network rule collection." + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the network rule." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description of the rule." + } + }, + "protocols": { + "type": "array", + "allowedValues": [ + "Any", + "ICMP", + "TCP", + "UDP" + ], + "metadata": { + "description": "Required. Array of AzureFirewallNetworkRuleProtocols." + } + }, + "destinationAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of destination IP addresses." + } + }, + "destinationFqdns": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of destination FQDNs." + } + }, + "destinationIpGroups": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of destination IP groups for this rule." + } + }, + "destinationPorts": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of destination ports." + } + }, + "sourceAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of source IP addresses for this rule." + } + }, + "sourceIpGroups": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of source IpGroups for this rule." + } + } + } + }, + "metadata": { + "description": "Required. Collection of rules used by a network rule collection." + } + } + }, + "metadata": { + "description": "Required. Properties of the azure firewall network rule collection." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Azure Firewall." + } + }, + "azureSkuTier": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Basic", + "Standard", + "Premium" + ], + "metadata": { + "description": "Optional. Tier of an Azure Firewall." + } + }, + "virtualNetworkResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "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." + } + }, + "publicIPResourceID": { + "type": "string", + "defaultValue": "", + "metadata": { + "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." + } + }, + "additionalPublicIpConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. This is to add any additional Public IP configurations on top of the Public IP with subnet IP configuration." + } + }, + "publicIPAddressObject": { + "type": "object", + "defaultValue": { + "name": "[format('{0}-pip', parameters('name'))]" + }, + "metadata": { + "description": "Optional. Specifies the properties of the Public IP to create and be used by the Firewall, if no existing public IP was provided." + } + }, + "managementIPResourceID": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Management Public IP resource ID to associate to the AzureFirewallManagementSubnet. If empty, then the Management Public IP that is created as part of this module will be applied to the AzureFirewallManagementSubnet." + } + }, + "managementIPAddressObject": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Specifies the properties of the Management Public IP to create and be used by Azure Firewall. If it's not provided and managementIPResourceID is empty, a '-mip' suffix will be appended to the Firewall's name." + } + }, + "applicationRuleCollections": { + "$ref": "#/definitions/applicationRuleCollectionType", + "metadata": { + "description": "Optional. Collection of application rule collections used by Azure Firewall." + } + }, + "networkRuleCollections": { + "$ref": "#/definitions/networkRuleCollectionType", + "metadata": { + "description": "Optional. Collection of network rule collections used by Azure Firewall." + } + }, + "natRuleCollections": { + "$ref": "#/definitions/natRuleCollectionType", + "metadata": { + "description": "Optional. Collection of NAT rule collections used by Azure Firewall." + } + }, + "firewallPolicyId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the Firewall Policy that should be attached." + } + }, + "hubIPAddresses": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Conditional. IP addresses associated with AzureFirewall. Required if `virtualHubId` is supplied." + } + }, + "virtualHubId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Conditional. The virtualHub resource ID to which the firewall belongs. Required if `virtualNetworkId` is empty." + } + }, + "threatIntelMode": { + "type": "string", + "defaultValue": "Deny", + "allowedValues": [ + "Alert", + "Deny", + "Off" + ], + "metadata": { + "description": "Optional. The operation mode for Threat Intel." + } + }, + "zones": { + "type": "array", + "defaultValue": [ + 1, + 2, + 3 + ], + "metadata": { + "description": "Optional. Zone numbers e.g. 1,2,3." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the Azure Firewall resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "additionalPublicIpConfigurationsVar", + "count": "[length(parameters('additionalPublicIpConfigurations'))]", + "input": { + "name": "[parameters('additionalPublicIpConfigurations')[copyIndex('additionalPublicIpConfigurationsVar')].name]", + "properties": { + "publicIPAddress": "[if(contains(parameters('additionalPublicIpConfigurations')[copyIndex('additionalPublicIpConfigurationsVar')], 'publicIPAddressResourceId'), createObject('id', parameters('additionalPublicIpConfigurations')[copyIndex('additionalPublicIpConfigurationsVar')].publicIPAddressResourceId), null())]" + } + } + }, + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "azureSkuName": "[if(empty(parameters('virtualNetworkResourceId')), 'AZFW_Hub', 'AZFW_VNet')]", + "requiresManagementIp": "[if(equals(parameters('azureSkuTier'), 'Basic'), true(), false())]", + "isCreateDefaultManagementIP": "[and(empty(parameters('managementIPResourceID')), variables('requiresManagementIp'))]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-azurefirewall.{0}.{1}', replace('0.5.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "azureFirewall": { + "type": "Microsoft.Network/azureFirewalls", + "apiVersion": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "zones": "[if(equals(length(parameters('zones')), 0), null(), parameters('zones'))]", + "tags": "[parameters('tags')]", + "properties": "[if(equals(variables('azureSkuName'), 'AZFW_VNet'), createObject('threatIntelMode', parameters('threatIntelMode'), 'firewallPolicy', if(not(empty(parameters('firewallPolicyId'))), createObject('id', parameters('firewallPolicyId')), null()), 'ipConfigurations', concat(createArray(createObject('name', if(not(empty(parameters('publicIPResourceID'))), last(split(parameters('publicIPResourceID'), '/')), reference('publicIPAddress').outputs.name.value), 'properties', union(createObject('subnet', createObject('id', format('{0}/subnets/AzureFirewallSubnet', parameters('virtualNetworkResourceId')))), if(or(not(empty(parameters('publicIPResourceID'))), not(empty(parameters('publicIPAddressObject')))), createObject('publicIPAddress', createObject('id', if(not(empty(parameters('publicIPResourceID'))), parameters('publicIPResourceID'), reference('publicIPAddress').outputs.resourceId.value))), createObject())))), variables('additionalPublicIpConfigurationsVar')), 'managementIpConfiguration', if(variables('requiresManagementIp'), createObject('name', if(not(empty(parameters('managementIPResourceID'))), last(split(parameters('managementIPResourceID'), '/')), reference('managementIPAddress').outputs.name.value), 'properties', createObject('subnet', createObject('id', format('{0}/subnets/AzureFirewallManagementSubnet', parameters('virtualNetworkResourceId'))), 'publicIPAddress', createObject('id', if(not(empty(parameters('managementIPResourceID'))), parameters('managementIPResourceID'), reference('managementIPAddress').outputs.resourceId.value)))), null()), 'sku', createObject('name', variables('azureSkuName'), 'tier', parameters('azureSkuTier')), 'applicationRuleCollections', coalesce(parameters('applicationRuleCollections'), createArray()), 'natRuleCollections', coalesce(parameters('natRuleCollections'), createArray()), 'networkRuleCollections', coalesce(parameters('networkRuleCollections'), createArray())), createObject('firewallPolicy', if(not(empty(parameters('firewallPolicyId'))), createObject('id', parameters('firewallPolicyId')), null()), 'sku', createObject('name', variables('azureSkuName'), 'tier', parameters('azureSkuTier')), 'hubIPAddresses', if(not(empty(parameters('hubIPAddresses'))), parameters('hubIPAddresses'), null()), 'virtualHub', if(not(empty(parameters('virtualHubId'))), createObject('id', parameters('virtualHubId')), null())))]", + "dependsOn": [ + "managementIPAddress", + "publicIPAddress" + ] + }, + "azureFirewall_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/azureFirewalls/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "azureFirewall" + ] + }, + "azureFirewall_diagnosticSettings": { + "copy": { + "name": "azureFirewall_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/azureFirewalls/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "azureFirewall" + ] + }, + "azureFirewall_roleAssignments": { + "copy": { + "name": "azureFirewall_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/azureFirewalls/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/azureFirewalls', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "azureFirewall" + ] + }, + "publicIPAddress": { + "condition": "[and(empty(parameters('publicIPResourceID')), equals(variables('azureSkuName'), 'AZFW_VNet'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Firewall-PIP', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('publicIPAddressObject').name]" + }, + "publicIpPrefixResourceId": "[if(contains(parameters('publicIPAddressObject'), 'publicIPPrefixResourceId'), if(not(empty(parameters('publicIPAddressObject').publicIPPrefixResourceId)), createObject('value', parameters('publicIPAddressObject').publicIPPrefixResourceId), createObject('value', '')), createObject('value', ''))]", + "publicIPAllocationMethod": "[if(contains(parameters('publicIPAddressObject'), 'publicIPAllocationMethod'), if(not(empty(parameters('publicIPAddressObject').publicIPAllocationMethod)), createObject('value', parameters('publicIPAddressObject').publicIPAllocationMethod), createObject('value', 'Static')), createObject('value', 'Static'))]", + "skuName": "[if(contains(parameters('publicIPAddressObject'), 'skuName'), if(not(empty(parameters('publicIPAddressObject').skuName)), createObject('value', parameters('publicIPAddressObject').skuName), createObject('value', 'Standard')), createObject('value', 'Standard'))]", + "skuTier": "[if(contains(parameters('publicIPAddressObject'), 'skuTier'), if(not(empty(parameters('publicIPAddressObject').skuTier)), createObject('value', parameters('publicIPAddressObject').skuTier), createObject('value', 'Regional')), createObject('value', 'Regional'))]", + "roleAssignments": "[if(contains(parameters('publicIPAddressObject'), 'roleAssignments'), if(not(empty(parameters('publicIPAddressObject').roleAssignments)), createObject('value', parameters('publicIPAddressObject').roleAssignments), createObject('value', createArray())), createObject('value', createArray()))]", + "diagnosticSettings": { + "value": "[tryGet(parameters('publicIPAddressObject'), 'diagnosticSettings')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "lock": { + "value": "[parameters('lock')]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('publicIPAddressObject'), 'tags'), parameters('tags'))]" + }, + "zones": { + "value": "[parameters('zones')]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(parameters('publicIPAddressObject'), 'enableTelemetry'), parameters('enableTelemetry'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "14450344965065009842" + }, + "name": "Public IP Addresses", + "description": "This module deploys a Public IP Address.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "dnsSettingsType": { + "type": "object", + "properties": { + "domainNameLabel": { + "type": "string", + "metadata": { + "description": "Required. 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." + } + }, + "domainNameLabelScope": { + "type": "string", + "allowedValues": [ + "", + "NoReuse", + "ResourceGroupReuse", + "SubscriptionReuse", + "TenantReuse" + ], + "metadata": { + "description": "Required. The domain name label scope. If a domain name label and a domain name label scope are specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system with a hashed value includes in FQDN." + } + }, + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "reverseFqdn": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + } + } + }, + "ddosSettingsType": { + "type": "object", + "properties": { + "ddosProtectionPlan": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the DDOS protection plan associated with the public IP address." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan associated with the public IP address." + } + }, + "protectionMode": { + "type": "string", + "allowedValues": [ + "Enabled" + ], + "metadata": { + "description": "Required. The DDoS protection policy customizations." + } + } + } + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Public IP Address." + } + }, + "publicIpPrefixResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "publicIPAllocationMethod": { + "type": "string", + "defaultValue": "Static", + "allowedValues": [ + "Dynamic", + "Static" + ], + "metadata": { + "description": "Optional. The public IP address allocation method." + } + }, + "zones": { + "type": "array", + "items": { + "type": "int" + }, + "defaultValue": [ + 1, + 2, + 3 + ], + "allowedValues": [ + 1, + 2, + 3 + ], + "metadata": { + "description": "Optional. A list of availability zones denoting the IP allocated for the resource needs to come from." + } + }, + "publicIPAddressVersion": { + "type": "string", + "defaultValue": "IPv4", + "allowedValues": [ + "IPv4", + "IPv6" + ], + "metadata": { + "description": "Optional. IP address version." + } + }, + "dnsSettings": { + "$ref": "#/definitions/dnsSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DNS settings of the public IP address." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Basic", + "Standard" + ], + "metadata": { + "description": "Optional. Name of a public IP address SKU." + } + }, + "skuTier": { + "type": "string", + "defaultValue": "Regional", + "allowedValues": [ + "Global", + "Regional" + ], + "metadata": { + "description": "Optional. Tier of a public IP address SKU." + } + }, + "ddosSettings": { + "$ref": "#/definitions/ddosSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan configuration associated with the public IP address." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "idleTimeoutInMinutes": { + "type": "int", + "defaultValue": 4, + "metadata": { + "description": "Optional. The idle timeout of the public IP address." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.5.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "publicIpAddress": { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2023-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('skuName')]", + "tier": "[parameters('skuTier')]" + }, + "zones": "[map(parameters('zones'), lambda('zone', string(lambdaVariables('zone'))))]", + "properties": { + "ddosSettings": "[parameters('ddosSettings')]", + "dnsSettings": "[parameters('dnsSettings')]", + "publicIPAddressVersion": "[parameters('publicIPAddressVersion')]", + "publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]", + "publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]", + "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]", + "ipTags": null + } + }, + "publicIpAddress_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_roleAssignments": { + "copy": { + "name": "publicIpAddress_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_diagnosticSettings": { + "copy": { + "name": "publicIpAddress_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the public IP address was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the public IP address." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the public IP address." + }, + "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('name'))]" + }, + "ipAddress": { + "type": "string", + "metadata": { + "description": "The public IP address of the public IP address resource." + }, + "value": "[coalesce(tryGet(reference('publicIpAddress'), 'ipAddress'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('publicIpAddress', '2023-09-01', 'full').location]" + } + } + } + } + }, + "managementIPAddress": { + "condition": "[and(variables('isCreateDefaultManagementIP'), equals(variables('azureSkuName'), 'AZFW_VNet'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Firewall-MIP', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": "[if(contains(parameters('managementIPAddressObject'), 'name'), if(not(empty(parameters('managementIPAddressObject').name)), createObject('value', parameters('managementIPAddressObject').name), createObject('value', format('{0}-mip', parameters('name')))), createObject('value', format('{0}-mip', parameters('name'))))]", + "publicIpPrefixResourceId": "[if(contains(parameters('managementIPAddressObject'), 'managementIPPrefixResourceId'), if(not(empty(parameters('managementIPAddressObject').managementIPPrefixResourceId)), createObject('value', parameters('managementIPAddressObject').managementIPPrefixResourceId), createObject('value', '')), createObject('value', ''))]", + "publicIPAllocationMethod": "[if(contains(parameters('managementIPAddressObject'), 'managementIPAllocationMethod'), if(not(empty(parameters('managementIPAddressObject').managementIPAllocationMethod)), createObject('value', parameters('managementIPAddressObject').managementIPAllocationMethod), createObject('value', 'Static')), createObject('value', 'Static'))]", + "skuName": "[if(contains(parameters('managementIPAddressObject'), 'skuName'), if(not(empty(parameters('managementIPAddressObject').skuName)), createObject('value', parameters('managementIPAddressObject').skuName), createObject('value', 'Standard')), createObject('value', 'Standard'))]", + "skuTier": "[if(contains(parameters('managementIPAddressObject'), 'skuTier'), if(not(empty(parameters('managementIPAddressObject').skuTier)), createObject('value', parameters('managementIPAddressObject').skuTier), createObject('value', 'Regional')), createObject('value', 'Regional'))]", + "roleAssignments": "[if(contains(parameters('managementIPAddressObject'), 'roleAssignments'), if(not(empty(parameters('managementIPAddressObject').roleAssignments)), createObject('value', parameters('managementIPAddressObject').roleAssignments), createObject('value', createArray())), createObject('value', createArray()))]", + "diagnosticSettings": { + "value": "[tryGet(parameters('managementIPAddressObject'), 'diagnosticSettings')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "tags": { + "value": "[coalesce(tryGet(parameters('managementIPAddressObject'), 'tags'), parameters('tags'))]" + }, + "zones": { + "value": "[parameters('zones')]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(parameters('managementIPAddressObject'), 'enableTelemetry'), parameters('enableTelemetry'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "14450344965065009842" + }, + "name": "Public IP Addresses", + "description": "This module deploys a Public IP Address.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "dnsSettingsType": { + "type": "object", + "properties": { + "domainNameLabel": { + "type": "string", + "metadata": { + "description": "Required. 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." + } + }, + "domainNameLabelScope": { + "type": "string", + "allowedValues": [ + "", + "NoReuse", + "ResourceGroupReuse", + "SubscriptionReuse", + "TenantReuse" + ], + "metadata": { + "description": "Required. The domain name label scope. If a domain name label and a domain name label scope are specified, an A DNS record is created for the public IP in the Microsoft Azure DNS system with a hashed value includes in FQDN." + } + }, + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "reverseFqdn": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + } + } + }, + "ddosSettingsType": { + "type": "object", + "properties": { + "ddosProtectionPlan": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the DDOS protection plan associated with the public IP address." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan associated with the public IP address." + } + }, + "protectionMode": { + "type": "string", + "allowedValues": [ + "Enabled" + ], + "metadata": { + "description": "Required. The DDoS protection policy customizations." + } + } + } + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Public IP Address." + } + }, + "publicIpPrefixResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "publicIPAllocationMethod": { + "type": "string", + "defaultValue": "Static", + "allowedValues": [ + "Dynamic", + "Static" + ], + "metadata": { + "description": "Optional. The public IP address allocation method." + } + }, + "zones": { + "type": "array", + "items": { + "type": "int" + }, + "defaultValue": [ + 1, + 2, + 3 + ], + "allowedValues": [ + 1, + 2, + 3 + ], + "metadata": { + "description": "Optional. A list of availability zones denoting the IP allocated for the resource needs to come from." + } + }, + "publicIPAddressVersion": { + "type": "string", + "defaultValue": "IPv4", + "allowedValues": [ + "IPv4", + "IPv6" + ], + "metadata": { + "description": "Optional. IP address version." + } + }, + "dnsSettings": { + "$ref": "#/definitions/dnsSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DNS settings of the public IP address." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Basic", + "Standard" + ], + "metadata": { + "description": "Optional. Name of a public IP address SKU." + } + }, + "skuTier": { + "type": "string", + "defaultValue": "Regional", + "allowedValues": [ + "Global", + "Regional" + ], + "metadata": { + "description": "Optional. Tier of a public IP address SKU." + } + }, + "ddosSettings": { + "$ref": "#/definitions/ddosSettingsType", + "nullable": true, + "metadata": { + "description": "Optional. The DDoS protection plan configuration associated with the public IP address." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "idleTimeoutInMinutes": { + "type": "int", + "defaultValue": 4, + "metadata": { + "description": "Optional. The idle timeout of the public IP address." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-publicipaddress.{0}.{1}', replace('0.5.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "publicIpAddress": { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2023-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "sku": { + "name": "[parameters('skuName')]", + "tier": "[parameters('skuTier')]" + }, + "zones": "[map(parameters('zones'), lambda('zone', string(lambdaVariables('zone'))))]", + "properties": { + "ddosSettings": "[parameters('ddosSettings')]", + "dnsSettings": "[parameters('dnsSettings')]", + "publicIPAddressVersion": "[parameters('publicIPAddressVersion')]", + "publicIPAllocationMethod": "[parameters('publicIPAllocationMethod')]", + "publicIPPrefix": "[if(not(empty(parameters('publicIpPrefixResourceId'))), createObject('id', parameters('publicIpPrefixResourceId')), null())]", + "idleTimeoutInMinutes": "[parameters('idleTimeoutInMinutes')]", + "ipTags": null + } + }, + "publicIpAddress_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_roleAssignments": { + "copy": { + "name": "publicIpAddress_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/publicIPAddresses', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + }, + "publicIpAddress_diagnosticSettings": { + "copy": { + "name": "publicIpAddress_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/publicIPAddresses/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "publicIpAddress" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the public IP address was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the public IP address." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the public IP address." + }, + "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('name'))]" + }, + "ipAddress": { + "type": "string", + "metadata": { + "description": "The public IP address of the public IP address resource." + }, + "value": "[coalesce(tryGet(reference('publicIpAddress'), 'ipAddress'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('publicIpAddress', '2023-09-01', 'full').location]" + } + } + } + } + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Azure Firewall." + }, + "value": "[resourceId('Microsoft.Network/azureFirewalls', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the Azure Firewall." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the Azure firewall was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "privateIp": { + "type": "string", + "metadata": { + "description": "The private IP of the Azure firewall." + }, + "value": "[if(contains(reference('azureFirewall'), 'ipConfigurations'), reference('azureFirewall').ipConfigurations[0].properties.privateIPAddress, '')]" + }, + "ipConfAzureFirewallSubnet": { + "type": "object", + "metadata": { + "description": "The Public IP configuration object for the Azure Firewall Subnet." + }, + "value": "[if(contains(reference('azureFirewall'), 'ipConfigurations'), reference('azureFirewall').ipConfigurations[0], createObject())]" + }, + "applicationRuleCollections": { + "type": "array", + "metadata": { + "description": "List of Application Rule Collections used by Azure Firewall." + }, + "value": "[coalesce(parameters('applicationRuleCollections'), createArray())]" + }, + "networkRuleCollections": { + "type": "array", + "metadata": { + "description": "List of Network Rule Collections used by Azure Firewall." + }, + "value": "[coalesce(parameters('networkRuleCollections'), createArray())]" + }, + "natRuleCollections": { + "type": "array", + "metadata": { + "description": "List of NAT rule collections used by Azure Firewall." + }, + "value": "[coalesce(parameters('natRuleCollections'), createArray())]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('azureFirewall', '2023-04-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "hubVirtualNetwork" + ] + }, + "hubAzureFirewallSubnet": { + "copy": { + "name": "hubAzureFirewallSubnet", + "count": "[length(items(coalesce(parameters('hubVirtualNetworks'), createObject())))]" + }, + "condition": "[items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value.enableAzureFirewall]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}-nafs', uniqueString(deployment().name, parameters('location')), items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "subnetName": { + "value": "AzureFirewallSubnet" + }, + "virtualNetworkName": { + "value": "[items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "13190798974838698070" + }, + "name": "Existing Virtual Network Subnets", + "description": "This module retrieves an existing Virtual Network Subnet.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "subnetName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The name of the subnet." + } + }, + "virtualNetworkName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The name of the virtual network." + } + } + }, + "resources": [], + "outputs": { + "subnetId": { + "type": "string", + "metadata": { + "description": "Subnet ID" + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName'))]" + }, + "addressPrefix": { + "type": "string", + "metadata": { + "description": "Subnet address prefix" + }, + "value": "[reference(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('subnetName')), '2024-01-01').addressPrefix]" + } + } + } + }, + "dependsOn": [ + "hubVirtualNetwork" + ] + }, + "hubAzureFirewallSubnetAssociation": { + "copy": { + "name": "hubAzureFirewallSubnetAssociation", + "count": "[length(items(coalesce(parameters('hubVirtualNetworks'), createObject())))]", + "mode": "serial", + "batchSize": 1 + }, + "condition": "[items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value.enableAzureFirewall]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-{1}-nafsa', uniqueString(deployment().name, parameters('location')), items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key)]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "AzureFirewallSubnet" + }, + "virtualNetworkName": { + "value": "[items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].key]" + }, + "addressPrefix": { + "value": "[reference(format('hubAzureFirewallSubnet[{0}]', copyIndex())).outputs.addressPrefix.value]" + }, + "routeTableResourceId": { + "value": "[reference(format('hubRouteTable[{0}]', copyIndex())).outputs.resourceId.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "11735652948112662202" + }, + "name": "Virtual Network Subnets", + "description": "This module deploys a Virtual Network Subnet.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The Name of the subnet resource." + } + }, + "virtualNetworkName": { + "type": "string", + "metadata": { + "description": "Required. The name of the parent virtual network. Required if the template is used in a standalone deployment." + } + }, + "addressPrefix": { + "type": "string", + "metadata": { + "description": "Required. The address prefix for the subnet." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the network security group to assign to the subnet." + } + }, + "routeTableResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpoints": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "delegations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The delegations to enable on the subnet." + } + }, + "natGatewayResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "Disabled", + "Enabled", + "" + ], + "metadata": { + "description": "Optional. Enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "Disabled", + "Enabled", + "" + ], + "metadata": { + "description": "Optional. Enable or disable apply network policies on private link service in the subnet." + } + }, + "addressPrefixes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of address prefixes for the subnet." + } + }, + "applicationGatewayIPConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application gateway IP configurations of virtual network resource." + } + }, + "ipAllocations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of IpAllocation which reference this subnet." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "virtualNetwork": { + "existing": true, + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2023-11-01", + "name": "[parameters('virtualNetworkName')]" + }, + "subnet": { + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "properties": { + "addressPrefix": "[parameters('addressPrefix')]", + "networkSecurityGroup": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('id', parameters('networkSecurityGroupResourceId')), null())]", + "routeTable": "[if(not(empty(parameters('routeTableResourceId'))), createObject('id', parameters('routeTableResourceId')), null())]", + "natGateway": "[if(not(empty(parameters('natGatewayResourceId'))), createObject('id', parameters('natGatewayResourceId')), null())]", + "serviceEndpoints": "[parameters('serviceEndpoints')]", + "delegations": "[parameters('delegations')]", + "privateEndpointNetworkPolicies": "[if(not(empty(parameters('privateEndpointNetworkPolicies'))), parameters('privateEndpointNetworkPolicies'), null())]", + "privateLinkServiceNetworkPolicies": "[if(not(empty(parameters('privateLinkServiceNetworkPolicies'))), parameters('privateLinkServiceNetworkPolicies'), null())]", + "addressPrefixes": "[parameters('addressPrefixes')]", + "applicationGatewayIPConfigurations": "[parameters('applicationGatewayIPConfigurations')]", + "ipAllocations": "[parameters('ipAllocations')]", + "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "subnet_roleAssignments": { + "copy": { + "name": "subnet_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}/subnets/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "subnet" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the subnet was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "subnetName": { + "type": "string", + "metadata": { + "description": "The name of the subnet." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the subnet." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name'))]" + }, + "subnetAddressPrefix": { + "type": "string", + "metadata": { + "description": "The address prefix for the subnet." + }, + "value": "[reference('subnet').addressPrefix]" + }, + "subnetAddressPrefixes": { + "type": "array", + "metadata": { + "description": "List of address prefixes for the subnet." + }, + "value": "[if(not(empty(parameters('addressPrefixes'))), reference('subnet').addressPrefixes, createArray())]" + } + } + } + }, + "dependsOn": [ + "hubAzureFirewall", + "hubAzureFirewallSubnet", + "[format('hubRouteTable[{0}]', copyIndex())]", + "hubVirtualNetwork" + ] + } + }, + "outputs": { + "hubVirtualNetworks": { + "type": "array", + "items": { + "type": "object" + }, + "metadata": { + "description": "Array of hub virtual network resources." + }, + "copy": { + "count": "[length(items(coalesce(parameters('hubVirtualNetworks'), createObject())))]", + "input": { + "resourceGroupName": "[reference(format('hubVirtualNetwork[{0}]', copyIndex())).outputs.resourceGroupName.value]", + "location": "[reference(format('hubVirtualNetwork[{0}]', copyIndex())).outputs.location.value]", + "name": "[reference(format('hubVirtualNetwork[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('hubVirtualNetwork[{0}]', copyIndex())).outputs.resourceId.value]" + } + } + }, + "hubBastions": { + "type": "array", + "items": { + "type": "object" + }, + "metadata": { + "description": "Array of hub bastion resources." + }, + "copy": { + "count": "[length(items(coalesce(parameters('hubVirtualNetworks'), createObject())))]", + "input": { + "resourceGroupName": "[reference(format('hubBastion[{0}]', copyIndex())).outputs.resourceGroupName.value]", + "location": "[reference(format('hubBastion[{0}]', copyIndex())).outputs.location.value]", + "name": "[reference(format('hubBastion[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('hubBastion[{0}]', copyIndex())).outputs.resourceId.value]" + } + } + }, + "hubAzureFirewalls": { + "type": "array", + "items": { + "type": "object" + }, + "metadata": { + "description": "Array of hub Azure Firewall resources." + }, + "copy": { + "count": "[length(items(coalesce(parameters('hubVirtualNetworks'), createObject())))]", + "input": { + "resourceGroupName": "[reference(format('hubAzureFirewall[{0}]', copyIndex())).outputs.resourceGroupName.value]", + "location": "[reference(format('hubAzureFirewall[{0}]', copyIndex())).outputs.location.value]", + "name": "[reference(format('hubAzureFirewall[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('hubAzureFirewall[{0}]', copyIndex())).outputs.resourceId.value]" + } + } + }, + "hubVirtualNetworkSubnets": { + "type": "array", + "metadata": { + "description": "The subnets of the hub virtual network." + }, + "copy": { + "count": "[length(items(coalesce(parameters('hubVirtualNetworks'), createObject())))]", + "input": "[reference(format('hubVirtualNetwork[{0}]', copyIndex())).outputs.subnetNames.value]" + } + } + } +} \ No newline at end of file diff --git a/avm/ptn/network/hub-networking/modules/getSubnet.bicep b/avm/ptn/network/hub-networking/modules/getSubnet.bicep new file mode 100644 index 0000000000..02a646a91c --- /dev/null +++ b/avm/ptn/network/hub-networking/modules/getSubnet.bicep @@ -0,0 +1,23 @@ +metadata name = 'Existing Virtual Network Subnets' +metadata description = 'This module retrieves an existing Virtual Network Subnet.' +metadata owner = 'Azure/module-maintainers' + +@description('Optional. The name of the subnet.') +param subnetName string = '' + +@description('Optional. The name of the virtual network.') +param virtualNetworkName string = '' + +resource vnet 'Microsoft.Network/virtualNetworks@2024-01-01' existing = { + name: virtualNetworkName + + resource subnet 'subnets@2024-01-01' existing = { + name: subnetName + } +} + +@description('Subnet ID') +output subnetId string = vnet::subnet.id + +@description('Subnet address prefix') +output addressPrefix string = vnet::subnet.properties.addressPrefix diff --git a/avm/ptn/network/hub-networking/modules/subnets.bicep b/avm/ptn/network/hub-networking/modules/subnets.bicep new file mode 100644 index 0000000000..49e9a4c21d --- /dev/null +++ b/avm/ptn/network/hub-networking/modules/subnets.bicep @@ -0,0 +1,185 @@ +metadata name = 'Virtual Network Subnets' +metadata description = 'This module deploys a Virtual Network Subnet.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The Name of the subnet resource.') +param name string + +@description('Required. 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 networkSecurityGroupResourceId string = '' + +@description('Optional. The resource ID of the route table to assign to the subnet.') +param routeTableResourceId 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 natGatewayResourceId 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 assignments to create.') +param roleAssignments roleAssignmentType + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Network Contributor': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '4d97b98b-1d4f-4787-a291-c67834d212e7' + ) + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) +} + +var formattedRoleAssignments = [ + for (roleAssignment, index) in (roleAssignments ?? []): union(roleAssignment, { + roleDefinitionId: builtInRoleNames[?roleAssignment.roleDefinitionIdOrName] ?? (contains( + roleAssignment.roleDefinitionIdOrName, + '/providers/Microsoft.Authorization/roleDefinitions/' + ) + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName)) + }) +] + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-11-01' existing = { + name: virtualNetworkName +} + +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' = { + name: name + parent: virtualNetwork + properties: { + addressPrefix: addressPrefix + networkSecurityGroup: !empty(networkSecurityGroupResourceId) + ? { + id: networkSecurityGroupResourceId + } + : null + routeTable: !empty(routeTableResourceId) + ? { + id: routeTableResourceId + } + : null + natGateway: !empty(natGatewayResourceId) + ? { + id: natGatewayResourceId + } + : 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 + } +} + +resource subnet_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(subnet.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) + properties: { + roleDefinitionId: roleAssignment.roleDefinitionId + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: subnet + } +] + +@description('The resource group the subnet was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the subnet.') +output subnetName string = subnet.name + +@description('The resource ID of the subnet.') +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 : [] + +// =============== // +// Definitions // +// =============== // + +type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') + roleDefinitionIdOrName: string + + @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') + principalId: string + + @description('Optional. The principal type of the assigned principal ID.') + principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')? + + @description('Optional. The description of the role assignment.') + description: string? + + @description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') + condition: string? + + @description('Optional. Version of the condition.') + conditionVersion: '2.0'? + + @description('Optional. The Resource Id of the delegated managed identity resource.') + delegatedManagedIdentityResourceId: string? +}[]? diff --git a/avm/ptn/network/hub-networking/modules/vnets.bicep b/avm/ptn/network/hub-networking/modules/vnets.bicep new file mode 100644 index 0000000000..78d6048a3f --- /dev/null +++ b/avm/ptn/network/hub-networking/modules/vnets.bicep @@ -0,0 +1,22 @@ +metadata name = 'Virtual Networks' +metadata description = 'This module deploys a Virtual Network.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The name of the parent virtual network. Required if the template is used in a standalone deployment.') +param name string + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-01-01' existing = { + name: name +} + +@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 = virtualNetwork.name + +@description('The resource ID of the virtual network peering.') +output resourceId string = virtualNetwork.id + +@description('The address space of the virtual network.') +output addressPrefix string = virtualNetwork.properties.addressSpace.addressPrefixes[0] diff --git a/avm/ptn/network/hub-networking/tests/e2e/defaults/main.test.bicep b/avm/ptn/network/hub-networking/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..86181df221 --- /dev/null +++ b/avm/ptn/network/hub-networking/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,47 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.hub-networking-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nhnmin' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2023-07-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + } + } +] diff --git a/avm/ptn/network/hub-networking/tests/e2e/max/main.test.bicep b/avm/ptn/network/hub-networking/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..5ff56e9d23 --- /dev/null +++ b/avm/ptn/network/hub-networking/tests/e2e/max/main.test.bicep @@ -0,0 +1,225 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set' +metadata description = 'This instance deploys the module with most of its features enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.hub-networking-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nhnmax' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2023-07-01' = { + name: resourceGroupName + location: resourceLocation +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +var addressPrefix = '10.0.0.0/16' +var addressPrefix2 = '10.1.0.0/16' + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + // You parameters go here + location: resourceLocation + hubVirtualNetworks: { + hub1: { + addressPrefixes: array(addressPrefix) + azureFirewallSettings: { + azureSkuTier: 'Standard' + enableTelemetry: true + location: resourceLocation + publicIPAddressObject: { + name: 'hub1-waf-pip' + } + threatIntelMode: 'Alert' + } + bastionHost: { + disableCopyPaste: true + enableFileCopy: false + enableIpConnect: false + enableShareableLink: false + scaleUnits: 2 + skuName: 'Standard' + } + dnsServers: ['10.0.1.4', '10.0.1.5'] + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + enableAzureFirewall: true + enableBastion: true + enablePeering: false + enableTelemetry: true + flowTimeoutInMinutes: 30 + location: resourceLocation + lock: { + kind: 'CanNotDelete' + name: 'hub1Lock' + } + peeringSettings: [ + { + allowForwardedTraffic: true + allowGatewayTransit: false + allowVirtualNetworkAccess: true + useRemoteGateways: false + remoteVirtualNetworkName: 'hub2' + } + ] + routes: [ + { + name: 'defaultRoute' + properties: { + addressPrefix: '0.0.0.0/0' + nextHopType: 'Internet' + } + } + ] + subnets: [ + { + name: 'GatewaySubnet' + addressPrefix: cidrSubnet(addressPrefix, 26, 0) + } + { + name: 'AzureFirewallSubnet' + addressPrefix: cidrSubnet(addressPrefix, 26, 1) + } + { + name: 'AzureBastionSubnet' + addressPrefix: cidrSubnet(addressPrefix, 26, 2) + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + vnetEncryption: false + vnetEncryptionEnforcement: 'AllowUnencrypted' + } + hub2: { + addressPrefixes: array(addressPrefix2) + azureFirewallSettings: { + azureSkuTier: 'Standard' + enableTelemetry: true + location: resourceLocation + publicIPAddressObject: { + name: 'hub2-waf-pip' + } + threatIntelMode: 'Alert' + zones: [ + 1 + 2 + 3 + ] + } + bastionHost: { + disableCopyPaste: true + enableFileCopy: false + enableIpConnect: false + enableShareableLink: false + scaleUnits: 2 + skuName: 'Standard' + } + enableAzureFirewall: true + enableBastion: true + enablePeering: false + enableTelemetry: false + flowTimeoutInMinutes: 10 + location: resourceLocation + lock: { + kind: 'CanNotDelete' + name: 'hub2Lock' + } + peeringSettings: [ + { + allowForwardedTraffic: true + allowGatewayTransit: false + allowVirtualNetworkAccess: true + useRemoteGateways: false + remoteVirtualNetworkName: 'hub1' + } + ] + routes: [ + { + name: 'defaultRoute' + properties: { + addressPrefix: '0.0.0.0/0' + nextHopType: 'Internet' + } + } + ] + subnets: [ + { + name: 'GatewaySubnet' + addressPrefix: cidrSubnet(addressPrefix2, 26, 0) + } + { + name: 'AzureFirewallSubnet' + addressPrefix: cidrSubnet(addressPrefix2, 26, 1) + } + { + name: 'AzureBastionSubnet' + addressPrefix: cidrSubnet(addressPrefix2, 26, 2) + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + vnetEncryption: false + vnetEncryptionEnforcement: 'AllowUnencrypted' + } + } + } + } +] diff --git a/avm/ptn/network/hub-networking/tests/e2e/waf-aligned/main.test.bicep b/avm/ptn/network/hub-networking/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..693e68fe09 --- /dev/null +++ b/avm/ptn/network/hub-networking/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,146 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.hub-networking-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nhnwaf' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2023-07-01' = { + name: resourceGroupName + location: resourceLocation +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +var addressPrefix = '10.0.0.0/16' + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + // You parameters go here + location: resourceLocation + hubVirtualNetworks: { + hub1: { + addressPrefixes: array(addressPrefix) + azureFirewallSettings: { + azureSkuTier: 'Standard' + enableTelemetry: true + location: resourceLocation + publicIPAddressObject: { + name: 'hub1PublicIp' + } + threatIntelMode: 'Alert' + zones: [ + 1 + 2 + 3 + ] + } + bastionHost: { + disableCopyPaste: true + enableFileCopy: false + enableIpConnect: false + enableShareableLink: false + scaleUnits: 2 + skuName: 'Standard' + } + enableAzureFirewall: true + enableBastion: true + enablePeering: false + enableTelemetry: true + flowTimeoutInMinutes: 30 + dnsServers: ['10.0.1.6', '10.0.1.7'] + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + location: resourceLocation + lock: { + kind: 'CanNotDelete' + name: 'hub1Lock' + } + routes: [ + { + name: 'defaultRoute' + properties: { + addressPrefix: '0.0.0.0/0' + nextHopType: 'Internet' + } + } + ] + subnets: [ + { + name: 'GatewaySubnet' + addressPrefix: cidrSubnet(addressPrefix, 26, 0) + } + { + name: 'AzureFirewallSubnet' + addressPrefix: cidrSubnet(addressPrefix, 26, 1) + } + { + name: 'AzureBastionSubnet' + addressPrefix: cidrSubnet(addressPrefix, 26, 2) + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + vnetEncryption: false + vnetEncryptionEnforcement: 'AllowUnencrypted' + } + } + } + } +] diff --git a/avm/ptn/network/hub-networking/version.json b/avm/ptn/network/hub-networking/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/ptn/network/hub-networking/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From 897149284afb4e39d7e2f928f650e8635d8e4366 Mon Sep 17 00:00:00 2001 From: hundredacres Date: Thu, 12 Sep 2024 23:33:32 -0700 Subject: [PATCH 19/57] feat: Add param for disabling access key (#3162) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Add support for disabling access key authorization. Added support to control updateChannel. Fixes #3127 Closes #3127 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.cache.redis](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.res.cache.redis.yml/badge.svg?branch=feat%2Fissues%2F3127)](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.res.cache.redis.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [X] Azure Verified Module updates: - [X] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [X] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Máté Barabás Co-authored-by: Rainer Halanek <61878316+rahalan@users.noreply.github.com> Co-authored-by: JFolberth --- avm/res/cache/redis/README.md | 9 +++++++++ avm/res/cache/redis/linked-servers/main.json | 2 +- avm/res/cache/redis/main.bicep | 4 ++++ avm/res/cache/redis/main.json | 12 ++++++++++-- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/avm/res/cache/redis/README.md b/avm/res/cache/redis/README.md index 53940a6aa5..fb766dff67 100644 --- a/avm/res/cache/redis/README.md +++ b/avm/res/cache/redis/README.md @@ -734,6 +734,7 @@ module redis 'br/public:avm/res/cache/redis:' = { | :-- | :-- | :-- | | [`capacity`](#parameter-capacity) | int | The size of the Redis cache to deploy. Valid values: for C (Basic/Standard) family (0, 1, 2, 3, 4, 5, 6), for P (Premium) family (1, 2, 3, 4). | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | +| [`disableAccessKeyAuthentication`](#parameter-disableaccesskeyauthentication) | bool | Disable authentication via access keys. | | [`enableNonSslPort`](#parameter-enablenonsslport) | bool | Specifies whether the non-ssl Redis server port (6379) is enabled. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`geoReplicationObject`](#parameter-georeplicationobject) | object | The geo-replication settings of the service. Requires a Premium SKU. Geo-replication is not supported on a cache with multiple replicas per primary. Secondary cache VM Size must be same or higher as compared to the primary cache VM Size. Geo-replication between a vnet and non vnet cache (and vice-a-versa) not supported. | @@ -930,6 +931,14 @@ Resource ID of the diagnostic log analytics workspace. For security reasons, it - Required: No - Type: string +### Parameter: `disableAccessKeyAuthentication` + +Disable authentication via access keys. + +- Required: No +- Type: bool +- Default: `False` + ### Parameter: `enableNonSslPort` Specifies whether the non-ssl Redis server port (6379) is enabled. diff --git a/avm/res/cache/redis/linked-servers/main.json b/avm/res/cache/redis/linked-servers/main.json index 67532e2795..1352156fef 100644 --- a/avm/res/cache/redis/linked-servers/main.json +++ b/avm/res/cache/redis/linked-servers/main.json @@ -98,4 +98,4 @@ "value": "[resourceGroup().name]" } } -} \ No newline at end of file +} diff --git a/avm/res/cache/redis/main.bicep b/avm/res/cache/redis/main.bicep index 09212df80b..9e866bb89e 100644 --- a/avm/res/cache/redis/main.bicep +++ b/avm/res/cache/redis/main.bicep @@ -20,6 +20,9 @@ param tags object? @description('Optional. The managed identity definition for this resource.') param managedIdentities managedIdentitiesType +@description('Optional. Disable authentication via access keys.') +param disableAccessKeyAuthentication bool = false + @description('Optional. Specifies whether the non-ssl Redis server port (6379) is enabled.') param enableNonSslPort bool = false @@ -181,6 +184,7 @@ resource redis 'Microsoft.Cache/redis@2024-03-01' = { tags: tags identity: identity properties: { + disableAccessKeyAuthentication: disableAccessKeyAuthentication enableNonSslPort: enableNonSslPort minimumTlsVersion: minimumTlsVersion publicNetworkAccess: !empty(publicNetworkAccess) diff --git a/avm/res/cache/redis/main.json b/avm/res/cache/redis/main.json index f9b3301dcb..222f86491e 100644 --- a/avm/res/cache/redis/main.json +++ b/avm/res/cache/redis/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "2147401251365362685" + "templateHash": "8427702382536251551" }, "name": "Redis Cache", "description": "This module deploys a Redis Cache.", @@ -514,6 +514,13 @@ "description": "Optional. The managed identity definition for this resource." } }, + "disableAccessKeyAuthentication": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Disable authentication via access keys." + } + }, "enableNonSslPort": { "type": "bool", "defaultValue": false, @@ -733,6 +740,7 @@ "tags": "[parameters('tags')]", "identity": "[variables('identity')]", "properties": { + "disableAccessKeyAuthentication": "[parameters('disableAccessKeyAuthentication')]", "enableNonSslPort": "[parameters('enableNonSslPort')]", "minimumTlsVersion": "[parameters('minimumTlsVersion')]", "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(not(empty(parameters('privateEndpoints'))), 'Disabled', null()))]", @@ -1801,4 +1809,4 @@ } } } -} \ No newline at end of file +} From 34c5bf10c4cb75fbd51db3fd4a030a7cb6d6bd7b Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Fri, 13 Sep 2024 09:23:52 +0200 Subject: [PATCH 20/57] fix: Updated hub-network output (#3259) ## Description Added missing resource group output to hub-network pattern module ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.network.hub-networking](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.ptn.network.hub-networking.yml/badge.svg?branch=users%2Falsehr%2FoutputTest&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.ptn.network.hub-networking.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [x] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --- avm/ptn/network/hub-networking/README.md | 1 + avm/ptn/network/hub-networking/main.bicep | 4 ++++ avm/ptn/network/hub-networking/main.json | 9 ++++++++- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/avm/ptn/network/hub-networking/README.md b/avm/ptn/network/hub-networking/README.md index 46c009b8c0..9dd17dbb8d 100644 --- a/avm/ptn/network/hub-networking/README.md +++ b/avm/ptn/network/hub-networking/README.md @@ -1663,6 +1663,7 @@ Location for all Resources. | `hubBastions` | array | Array of hub bastion resources. | | `hubVirtualNetworks` | array | Array of hub virtual network resources. | | `hubVirtualNetworkSubnets` | array | The subnets of the hub virtual network. | +| `resourceGroupName` | string | The resource group the resources were deployed into. | ## Cross-referenced modules diff --git a/avm/ptn/network/hub-networking/main.bicep b/avm/ptn/network/hub-networking/main.bicep index 8406fc1cc1..981cdd4eff 100644 --- a/avm/ptn/network/hub-networking/main.bicep +++ b/avm/ptn/network/hub-networking/main.bicep @@ -23,6 +23,7 @@ var hubVirtualNetworkPeerings = [for (hub, index) in items(hubVirtualNetworks ?? // Resources // // ============== // +#disable-next-line no-deployments-resources resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { name: '46d3xbcp.ptn.network-hubnetworking.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' properties: { @@ -267,6 +268,9 @@ output hubVirtualNetworkSubnets array = [ for (hub, index) in items(hubVirtualNetworks ?? {}): hubVirtualNetwork[index].outputs.subnetNames ] +@description('The resource group the resources were deployed into.') +output resourceGroupName string = resourceGroup().name + // ================ // // Definitions // // ================ // diff --git a/avm/ptn/network/hub-networking/main.json b/avm/ptn/network/hub-networking/main.json index b2149b348c..8fb5cb887b 100644 --- a/avm/ptn/network/hub-networking/main.json +++ b/avm/ptn/network/hub-networking/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "4469274513824919483" + "templateHash": "9188161100861636713" }, "name": "Hub Networking", "description": "This module is designed to simplify the creation of multi-region hub networks in Azure. It will create a number of virtual networks and subnets, and optionally peer them together in a mesh topology with routing.", @@ -6894,6 +6894,13 @@ "count": "[length(items(coalesce(parameters('hubVirtualNetworks'), createObject())))]", "input": "[reference(format('hubVirtualNetwork[{0}]', copyIndex())).outputs.subnetNames.value]" } + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the resources were deployed into." + }, + "value": "[resourceGroup().name]" } } } \ No newline at end of file From 9dacc4c55724e2ab4551a99eab0b4e772cf8c2c9 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Fri, 13 Sep 2024 16:40:08 +0200 Subject: [PATCH 21/57] feat: Added logic to add available role names to readme (#706) ## Description Added logic to add available role names to readme At this time, this works only for modules that use the Language Server Version 2 @eriqua, @jtracey93 & @ChrisSidebotham this PR is fundamentally ready for review - but I want to get your approval first before generating all readme's E.g. ![image](https://github.com/Azure/bicep-registry-modules/assets/5365358/90e8a99f-0b24-4521-8fb8-1d17f8b8b0ed) | Pipeline | | - | [![avm.res.aad.domain-service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.aad.domain-service.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.aad.domain-service.yml) [![avm.res.alerts-management.action-rule](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.alerts-management.action-rule.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.alerts-management.action-rule.yml) [![avm.res.analysis-services.server](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.analysis-services.server.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.analysis-services.server.yml) [![avm.res.api-management.service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.api-management.service.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.api-management.service.yml) [![avm.res.app-configuration.configuration-store](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app-configuration.configuration-store.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app-configuration.configuration-store.yml) [![avm.res.app.container-app](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.container-app.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.container-app.yml) [![avm.res.app.job](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.job.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.job.yml) [![avm.res.app.managed-environment](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.managed-environment.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.managed-environment.yml) [![avm.res.automation.automation-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.automation.automation-account.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.automation.automation-account.yml) [![avm.res.batch.batch-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.batch.batch-account.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.batch.batch-account.yml) [![avm.res.cache.redis](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cache.redis.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cache.redis.yml) [![avm.res.cdn.profile](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cdn.profile.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cdn.profile.yml) [![avm.res.cognitive-services.account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cognitive-services.account.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.cognitive-services.account.yml) [![avm.res.communication.communication-service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.communication.communication-service.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.communication.communication-service.yml) [![avm.res.communication.email-service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.communication.email-service.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.communication.email-service.yml) [![avm.res.compute.availability-set](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.availability-set.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.availability-set.yml) [![avm.res.compute.disk-encryption-set](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.disk-encryption-set.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.disk-encryption-set.yml) [![avm.res.compute.disk](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.disk.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.disk.yml) [![avm.res.compute.gallery](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.gallery.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.gallery.yml) [![avm.res.compute.image](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.image.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.image.yml) [![avm.res.compute.proximity-placement-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.proximity-placement-group.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.proximity-placement-group.yml) [![avm.res.compute.ssh-public-key](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.ssh-public-key.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.ssh-public-key.yml) [![avm.res.compute.virtual-machine-scale-set](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine-scale-set.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine-scale-set.yml) [![avm.res.compute.virtual-machine](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.compute.virtual-machine.yml) [![avm.res.consumption.budget](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.consumption.budget.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.consumption.budget.yml) [![avm.res.container-instance.container-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-instance.container-group.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-instance.container-group.yml) [![avm.res.container-registry.registry](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-registry.registry.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-registry.registry.yml) [![avm.res.container-service.managed-cluster](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.container-service.managed-cluster.yml) (unrelated, WAF) [![avm.res.data-factory.factory](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.data-factory.factory.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.data-factory.factory.yml) [![avm.res.data-protection.backup-vault](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.data-protection.backup-vault.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.data-protection.backup-vault.yml) [![avm.res.databricks.access-connector](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.databricks.access-connector.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.databricks.access-connector.yml) [![avm.res.databricks.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.databricks.workspace.yml) [![avm.res.db-for-my-sql.flexible-server](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.db-for-my-sql.flexible-server.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.db-for-my-sql.flexible-server.yml) [![avm.res.db-for-postgre-sql.flexible-server](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.db-for-postgre-sql.flexible-server.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.db-for-postgre-sql.flexible-server.yml) [![avm.res.desktop-virtualization.application-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.application-group.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.application-group.yml) [![avm.res.desktop-virtualization.host-pool](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.host-pool.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.host-pool.yml) [![avm.res.desktop-virtualization.scaling-plan](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.scaling-plan.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.scaling-plan.yml) [![avm.res.desktop-virtualization.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.workspace.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.desktop-virtualization.workspace.yml) [![avm.res.dev-test-lab.lab](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.dev-test-lab.lab.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.dev-test-lab.lab.yml) [![avm.res.digital-twins.digital-twins-instance](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.digital-twins.digital-twins-instance.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.digital-twins.digital-twins-instance.yml) [![avm.res.document-db.database-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.document-db.database-account.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.document-db.database-account.yml) [![avm.res.event-grid.domain](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.domain.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.domain.yml) [![avm.res.event-grid.namespace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.namespace.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.namespace.yml) [![avm.res.event-grid.system-topic](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.system-topic.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.system-topic.yml) [![avm.res.event-grid.topic](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.topic.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-grid.topic.yml) [![avm.res.event-hub.namespace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-hub.namespace.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.event-hub.namespace.yml) [![avm.res.health-bot.health-bot](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.health-bot.health-bot.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.health-bot.health-bot.yml) [![avm.res.healthcare-apis.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.healthcare-apis.workspace.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.healthcare-apis.workspace.yml) [![avm.res.hybrid-compute.machine](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.hybrid-compute.machine.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.hybrid-compute.machine.yml) [![avm.res.insights.action-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.action-group.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.action-group.yml) [![avm.res.insights.activity-log-alert](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.activity-log-alert.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.activity-log-alert.yml) [![avm.res.insights.component](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.component.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.component.yml) [![avm.res.insights.data-collection-endpoint](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.data-collection-endpoint.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.data-collection-endpoint.yml) [![avm.res.insights.data-collection-rule](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.data-collection-rule.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.data-collection-rule.yml) [![avm.res.insights.diagnostic-setting](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.diagnostic-setting.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.diagnostic-setting.yml) [![avm.res.insights.metric-alert](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.metric-alert.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.metric-alert.yml) [![avm.res.insights.private-link-scope](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.private-link-scope.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.private-link-scope.yml) [![avm.res.insights.scheduled-query-rule](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.scheduled-query-rule.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.scheduled-query-rule.yml) [![avm.res.insights.webtest](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.webtest.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.insights.webtest.yml) [![avm.res.key-vault.vault](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.key-vault.vault.yml) [![avm.res.kubernetes-configuration.extension](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.kubernetes-configuration.extension.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.kubernetes-configuration.extension.yml) [![avm.res.kubernetes-configuration.flux-configuration](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.kubernetes-configuration.flux-configuration.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.kubernetes-configuration.flux-configuration.yml) [![avm.res.kusto.cluster](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.kusto.cluster.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.kusto.cluster.yml) [![avm.res.load-test-service.load-test](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.load-test-service.load-test.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.load-test-service.load-test.yml) [![avm.res.logic.workflow](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.logic.workflow.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.logic.workflow.yml) [![avm.res.machine-learning-services.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.machine-learning-services.workspace.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.machine-learning-services.workspace.yml) [![avm.res.maintenance.maintenance-configuration](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.maintenance.maintenance-configuration.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.maintenance.maintenance-configuration.yml) [![avm.res.managed-identity.user-assigned-identity](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.managed-identity.user-assigned-identity.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.managed-identity.user-assigned-identity.yml) [![avm.res.managed-services.registration-definition](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.managed-services.registration-definition.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.managed-services.registration-definition.yml) [![avm.res.management.management-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.management.management-group.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.management.management-group.yml) [![avm.res.net-app.net-app-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.net-app.net-app-account.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.net-app.net-app-account.yml) [![avm.res.network.application-gateway-web-application-firewall-policy](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway-web-application-firewall-policy.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway-web-application-firewall-policy.yml) [![avm.res.network.application-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-gateway.yml) [![avm.res.network.application-security-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-security-group.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.application-security-group.yml) [![avm.res.network.azure-firewall](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.azure-firewall.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.azure-firewall.yml) [![avm.res.network.bastion-host](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.bastion-host.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.bastion-host.yml) [![avm.res.network.connection](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.connection.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.connection.yml) [![avm.res.network.ddos-protection-plan](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.ddos-protection-plan.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.ddos-protection-plan.yml) [![avm.res.network.dns-forwarding-ruleset](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-forwarding-ruleset.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-forwarding-ruleset.yml) [![avm.res.network.dns-resolver](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-resolver.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-resolver.yml) [![avm.res.network.dns-zone](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-zone.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.dns-zone.yml) [![avm.res.network.express-route-circuit](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.express-route-circuit.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.express-route-circuit.yml) [![avm.res.network.express-route-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.express-route-gateway.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.express-route-gateway.yml) [![avm.res.network.firewall-policy](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.firewall-policy.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.firewall-policy.yml) [![avm.res.network.front-door-web-application-firewall-policy](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.front-door-web-application-firewall-policy.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.front-door-web-application-firewall-policy.yml) [![avm.res.network.front-door](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.front-door.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.front-door.yml) [![avm.res.network.ip-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.ip-group.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.ip-group.yml) [![avm.res.network.load-balancer](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.load-balancer.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.load-balancer.yml) [![avm.res.network.local-network-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.local-network-gateway.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.local-network-gateway.yml) [![avm.res.network.nat-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.nat-gateway.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.nat-gateway.yml) [![avm.res.network.network-interface](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-interface.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-interface.yml) [![avm.res.network.network-manager](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-manager.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-manager.yml) [![avm.res.network.network-security-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-security-group.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-security-group.yml) [![avm.res.network.network-watcher](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-watcher.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.network-watcher.yml) [![avm.res.network.private-dns-zone](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-dns-zone.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-dns-zone.yml) [![avm.res.network.private-endpoint](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-endpoint.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-endpoint.yml) [![avm.res.network.private-link-service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-link-service.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.private-link-service.yml) [![avm.res.network.public-ip-address](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-address.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-address.yml) [![avm.res.network.public-ip-prefix](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-prefix.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-prefix.yml) [![avm.res.network.route-table](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.route-table.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.route-table.yml) [![avm.res.network.service-endpoint-policy](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.service-endpoint-policy.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.service-endpoint-policy.yml) [![avm.res.network.trafficmanagerprofile](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.trafficmanagerprofile.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.trafficmanagerprofile.yml) [![avm.res.network.virtual-hub](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-hub.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-hub.yml) [![avm.res.network.virtual-network-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network-gateway.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network-gateway.yml) [![avm.res.network.virtual-network](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network.yml) [![avm.res.network.virtual-wan](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-wan.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.virtual-wan.yml) [![avm.res.network.vpn-gateway](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.vpn-gateway.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.vpn-gateway.yml) [![avm.res.network.vpn-site](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.vpn-site.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.network.vpn-site.yml) [![avm.res.operational-insights.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.operational-insights.workspace.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.operational-insights.workspace.yml) [![avm.res.operations-management.solution](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.operations-management.solution.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.operations-management.solution.yml) [![avm.res.portal.dashboard](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.portal.dashboard.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.portal.dashboard.yml) [![avm.res.power-bi-dedicated.capacity](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.power-bi-dedicated.capacity.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.power-bi-dedicated.capacity.yml) [![avm.res.purview.account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.purview.account.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.purview.account.yml) [![avm.res.recovery-services.vault](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.recovery-services.vault.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.recovery-services.vault.yml) [![avm.res.relay.namespace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.relay.namespace.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.relay.namespace.yml) [![avm.res.resource-graph.query](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resource-graph.query.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resource-graph.query.yml) [![avm.res.resources.deployment-script](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resources.deployment-script.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resources.deployment-script.yml) [![avm.res.resources.resource-group](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resources.resource-group.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.resources.resource-group.yml) [![avm.res.search.search-service](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.search.search-service.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.search.search-service.yml) [![avm.res.service-bus.namespace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.service-bus.namespace.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.service-bus.namespace.yml) [![avm.res.service-fabric.cluster](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.service-fabric.cluster.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.service-fabric.cluster.yml) [![avm.res.signal-r-service.signal-r](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.signal-r.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.signal-r.yml) [![avm.res.signal-r-service.web-pub-sub](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.web-pub-sub.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.signal-r-service.web-pub-sub.yml) [![avm.res.sql.instance-pool](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.instance-pool.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.instance-pool.yml) [![avm.res.sql.managed-instance](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.managed-instance.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.managed-instance.yml) (unrelated, WAF) [![avm.res.sql.server](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.server.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.sql.server.yml) [![avm.res.storage.storage-account](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml) [![avm.res.synapse.private-link-hub](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.synapse.private-link-hub.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.synapse.private-link-hub.yml) [![avm.res.synapse.workspace](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.synapse.workspace.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.synapse.workspace.yml) [![avm.res.virtual-machine-images.image-template](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.virtual-machine-images.image-template.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.virtual-machine-images.image-template.yml) [![avm.res.web.connection](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.connection.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.connection.yml) [![avm.res.web.hosting-environment](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.hosting-environment.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.hosting-environment.yml) (unrelated, WAF) [![avm.res.web.serverfarm](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.serverfarm.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.serverfarm.yml) [![avm.res.web.site](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.site.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.site.yml) [![avm.res.web.static-site](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.static-site.yml/badge.svg?branch=users%2Falsehr%2Freadme_Roles&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.web.static-site.yml) --------- Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> Co-authored-by: ChrisSidebotham-MSFT <48600046+ChrisSidebotham@users.noreply.github.com> --- avm/res/aad/domain-service/README.md | 6 + .../alerts-management/action-rule/README.md | 6 + avm/res/analysis-services/server/README.md | 6 + avm/res/api-management/service/README.md | 10 ++ .../configuration-store/README.md | 21 +++ avm/res/app/container-app/README.md | 7 + avm/res/app/job/README.md | 7 + avm/res/app/managed-environment/README.md | 6 + .../automation/automation-account/README.md | 21 +++ avm/res/batch/batch-account/README.md | 17 +++ avm/res/cache/redis/README.md | 18 +++ avm/res/cdn/profile/README.md | 10 ++ avm/res/cognitive-services/account/README.md | 41 ++++++ .../communication-service/README.md | 6 + avm/res/communication/email-service/README.md | 6 + .../email-service/domain/README.md | 6 + avm/res/compute/availability-set/README.md | 10 ++ avm/res/compute/disk-encryption-set/README.md | 11 ++ avm/res/compute/disk/README.md | 11 ++ avm/res/compute/gallery/README.md | 7 + avm/res/compute/gallery/application/README.md | 7 + avm/res/compute/gallery/image/README.md | 7 + avm/res/compute/image/README.md | 6 + .../proximity-placement-group/README.md | 6 + avm/res/compute/ssh-public-key/README.md | 6 + .../virtual-machine-scale-set/README.md | 19 +++ avm/res/compute/virtual-machine/README.md | 19 +++ avm/res/container-registry/registry/README.md | 23 +++ .../managed-cluster/README.md | 20 +++ avm/res/data-factory/factory/README.md | 18 +++ .../data-protection/backup-vault/README.md | 9 ++ avm/res/databricks/access-connector/README.md | 6 + avm/res/databricks/workspace/README.md | 12 ++ .../db-for-my-sql/flexible-server/README.md | 7 + .../flexible-server/README.md | 6 + .../application-group/README.md | 23 +++ .../host-pool/README.md | 34 +++++ .../scaling-plan/README.md | 20 +++ .../workspace/README.md | 31 ++++ avm/res/dev-test-lab/lab/README.md | 9 ++ .../digital-twins-instance/README.md | 19 +++ .../document-db/database-account/README.md | 22 +++ avm/res/event-grid/domain/README.md | 21 +++ avm/res/event-grid/namespace/README.md | 25 ++++ .../namespace/topic-space/README.md | 14 ++ avm/res/event-grid/namespace/topic/README.md | 14 ++ .../topic/event-subscription/README.md | 14 ++ avm/res/event-grid/system-topic/README.md | 10 ++ avm/res/event-grid/topic/README.md | 21 +++ avm/res/event-hub/namespace/README.md | 20 +++ .../event-hub/namespace/eventhub/README.md | 9 ++ avm/res/health-bot/health-bot/README.md | 6 + avm/res/healthcare-apis/workspace/README.md | 15 ++ .../workspace/fhirservice/README.md | 15 ++ avm/res/hybrid-compute/machine/README.md | 10 ++ avm/res/insights/action-group/README.md | 6 + avm/res/insights/activity-log-alert/README.md | 6 + avm/res/insights/component/README.md | 10 ++ .../data-collection-endpoint/README.md | 6 + .../insights/data-collection-rule/README.md | 6 + avm/res/insights/metric-alert/README.md | 6 + avm/res/insights/private-link-scope/README.md | 26 ++++ .../insights/scheduled-query-rule/README.md | 6 + avm/res/insights/webtest/README.md | 6 + avm/res/key-vault/vault/README.md | 50 +++++++ avm/res/key-vault/vault/key/README.md | 12 ++ avm/res/key-vault/vault/secret/README.md | 11 ++ avm/res/kusto/cluster/README.md | 15 ++ avm/res/load-test-service/load-test/README.md | 6 + avm/res/logic/workflow/README.md | 8 ++ .../workspace/README.md | 21 +++ .../maintenance-configuration/README.md | 7 + .../user-assigned-identity/README.md | 8 ++ avm/res/net-app/net-app-account/README.md | 6 + .../net-app-account/capacity-pool/README.md | 6 + .../capacity-pool/volume/README.md | 6 + avm/res/network/application-gateway/README.md | 17 +++ .../application-security-group/README.md | 6 + avm/res/network/azure-firewall/README.md | 6 + avm/res/network/bastion-host/README.md | 6 + .../network/ddos-protection-plan/README.md | 6 + .../network/dns-forwarding-ruleset/README.md | 9 ++ avm/res/network/dns-resolver/README.md | 11 ++ avm/res/network/dns-zone/README.md | 132 ++++++++++++++++++ avm/res/network/dns-zone/a/README.md | 12 ++ avm/res/network/dns-zone/aaaa/README.md | 12 ++ avm/res/network/dns-zone/caa/README.md | 12 ++ avm/res/network/dns-zone/cname/README.md | 12 ++ avm/res/network/dns-zone/mx/README.md | 12 ++ avm/res/network/dns-zone/ns/README.md | 12 ++ avm/res/network/dns-zone/ptr/README.md | 12 ++ avm/res/network/dns-zone/soa/README.md | 12 ++ avm/res/network/dns-zone/srv/README.md | 12 ++ avm/res/network/dns-zone/txt/README.md | 12 ++ .../network/express-route-circuit/README.md | 7 + .../network/express-route-gateway/README.md | 7 + .../README.md | 6 + avm/res/network/front-door/README.md | 7 + avm/res/network/ip-group/README.md | 7 + avm/res/network/load-balancer/README.md | 7 + .../network/local-network-gateway/README.md | 7 + avm/res/network/nat-gateway/README.md | 7 + avm/res/network/network-interface/README.md | 9 ++ avm/res/network/network-manager/README.md | 10 ++ .../network/network-security-group/README.md | 7 + avm/res/network/network-watcher/README.md | 7 + avm/res/network/private-dns-zone/README.md | 71 ++++++++++ avm/res/network/private-dns-zone/a/README.md | 8 ++ .../network/private-dns-zone/aaaa/README.md | 8 ++ .../network/private-dns-zone/cname/README.md | 8 ++ avm/res/network/private-dns-zone/mx/README.md | 8 ++ .../network/private-dns-zone/ptr/README.md | 8 ++ .../network/private-dns-zone/soa/README.md | 8 ++ .../network/private-dns-zone/srv/README.md | 8 ++ .../network/private-dns-zone/txt/README.md | 8 ++ avm/res/network/private-endpoint/README.md | 11 ++ .../network/private-link-service/README.md | 8 ++ avm/res/network/public-ip-address/README.md | 11 ++ avm/res/network/public-ip-prefix/README.md | 7 + avm/res/network/route-table/README.md | 7 + .../network/service-endpoint-policy/README.md | 7 + .../network/trafficmanagerprofile/README.md | 8 ++ .../network/virtual-network-gateway/README.md | 7 + avm/res/network/virtual-network/README.md | 14 ++ .../network/virtual-network/subnet/README.md | 7 + avm/res/network/virtual-wan/README.md | 7 + avm/res/network/vpn-site/README.md | 7 + .../operational-insights/workspace/README.md | 12 ++ .../workspace/table/README.md | 10 ++ avm/res/portal/dashboard/README.md | 6 + avm/res/power-bi-dedicated/capacity/README.md | 8 ++ avm/res/purview/account/README.md | 18 +++ avm/res/recovery-services/vault/README.md | 23 +++ avm/res/relay/namespace/README.md | 20 +++ .../namespace/hybrid-connection/README.md | 9 ++ avm/res/relay/namespace/wcf-relay/README.md | 9 ++ avm/res/resource-graph/query/README.md | 6 + avm/res/resources/deployment-script/README.md | 6 + avm/res/search/search-service/README.md | 20 +++ avm/res/service-bus/namespace/README.md | 38 +++++ avm/res/service-bus/namespace/queue/README.md | 9 ++ avm/res/service-bus/namespace/topic/README.md | 9 ++ avm/res/service-fabric/cluster/README.md | 6 + avm/res/signal-r-service/signal-r/README.md | 25 ++++ .../signal-r-service/web-pub-sub/README.md | 25 ++++ avm/res/sql/managed-instance/README.md | 13 ++ avm/res/sql/server/README.md | 24 ++++ avm/res/storage/storage-account/README.md | 36 +++++ .../blob-service/container/README.md | 14 ++ .../queue-service/queue/README.md | 14 ++ .../table-service/table/README.md | 12 ++ avm/res/synapse/private-link-hub/README.md | 17 +++ avm/res/synapse/workspace/README.md | 18 +++ .../image-template/README.md | 6 + avm/res/web/connection/README.md | 6 + avm/res/web/hosting-environment/README.md | 6 + avm/res/web/serverfarm/README.md | 8 ++ avm/res/web/site/README.md | 9 ++ avm/res/web/site/slot/README.md | 20 +++ avm/res/web/static-site/README.md | 19 +++ .../sharedScripts/Get-NestedResourceList.ps1 | 11 +- .../sharedScripts/Set-ModuleReadMe.ps1 | 31 +++- 162 files changed, 2145 insertions(+), 5 deletions(-) diff --git a/avm/res/aad/domain-service/README.md b/avm/res/aad/domain-service/README.md index 9aa32399c5..1a8c5627a8 100644 --- a/avm/res/aad/domain-service/README.md +++ b/avm/res/aad/domain-service/README.md @@ -645,6 +645,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/alerts-management/action-rule/README.md b/avm/res/alerts-management/action-rule/README.md index 72c9e58af6..658f855769 100644 --- a/avm/res/alerts-management/action-rule/README.md +++ b/avm/res/alerts-management/action-rule/README.md @@ -603,6 +603,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/analysis-services/server/README.md b/avm/res/analysis-services/server/README.md index 18ad72dda3..32cb2ff12e 100644 --- a/avm/res/analysis-services/server/README.md +++ b/avm/res/analysis-services/server/README.md @@ -669,6 +669,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/api-management/service/README.md b/avm/res/api-management/service/README.md index fa41e81140..c219b2de19 100644 --- a/avm/res/api-management/service/README.md +++ b/avm/res/api-management/service/README.md @@ -1742,6 +1742,16 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'API Management Developer Portal Content Editor'` + - `'API Management Service Contributor'` + - `'API Management Service Operator Role'` + - `'API Management Service Reader Role'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/app-configuration/configuration-store/README.md b/avm/res/app-configuration/configuration-store/README.md index 06d8a2d62d..62362ac7a1 100644 --- a/avm/res/app-configuration/configuration-store/README.md +++ b/avm/res/app-configuration/configuration-store/README.md @@ -1303,6 +1303,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1435,6 +1446,16 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'App Compliance Automation Administrator'` + - `'App Compliance Automation Reader'` + - `'App Configuration Data Owner'` + - `'App Configuration Data Reader'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/app/container-app/README.md b/avm/res/app/container-app/README.md index 9ac975e122..a032f3411f 100644 --- a/avm/res/app/container-app/README.md +++ b/avm/res/app/container-app/README.md @@ -1409,6 +1409,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'ContainerApp Reader'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/app/job/README.md b/avm/res/app/job/README.md index a0d091b780..792068064b 100644 --- a/avm/res/app/job/README.md +++ b/avm/res/app/job/README.md @@ -1741,6 +1741,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'ContainerApp Reader'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/app/managed-environment/README.md b/avm/res/app/managed-environment/README.md index 66e8fe2364..75b860ddd7 100644 --- a/avm/res/app/managed-environment/README.md +++ b/avm/res/app/managed-environment/README.md @@ -778,6 +778,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/automation/automation-account/README.md b/avm/res/automation/automation-account/README.md index 8aa19e6b65..d6b50e257b 100644 --- a/avm/res/automation/automation-account/README.md +++ b/avm/res/automation/automation-account/README.md @@ -1853,6 +1853,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1973,6 +1984,16 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Automation Contributor'` + - `'Automation Job Operator'` + - `'Automation Operator'` + - `'Automation Runbook Operator'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/batch/batch-account/README.md b/avm/res/batch/batch-account/README.md index 7b725694d8..788df94957 100644 --- a/avm/res/batch/batch-account/README.md +++ b/avm/res/batch/batch-account/README.md @@ -1407,6 +1407,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1527,6 +1538,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/cache/redis/README.md b/avm/res/cache/redis/README.md index fb766dff67..addd537447 100644 --- a/avm/res/cache/redis/README.md +++ b/avm/res/cache/redis/README.md @@ -1341,6 +1341,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1507,6 +1518,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Redis Cache Contributor'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/cdn/profile/README.md b/avm/res/cdn/profile/README.md index ccaf732ba3..369cadf61b 100644 --- a/avm/res/cdn/profile/README.md +++ b/avm/res/cdn/profile/README.md @@ -740,6 +740,16 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'CDN Endpoint Contributor'` + - `'CDN Endpoint Reader'` + - `'CDN Profile Contributor'` + - `'CDN Profile Reader'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/cognitive-services/account/README.md b/avm/res/cognitive-services/account/README.md index 9838b6a3e6..32e22ce46f 100644 --- a/avm/res/cognitive-services/account/README.md +++ b/avm/res/cognitive-services/account/README.md @@ -1869,6 +1869,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -2010,6 +2021,36 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Cognitive Services Contributor'` + - `'Cognitive Services Custom Vision Contributor'` + - `'Cognitive Services Custom Vision Deployment'` + - `'Cognitive Services Custom Vision Labeler'` + - `'Cognitive Services Custom Vision Reader'` + - `'Cognitive Services Custom Vision Trainer'` + - `'Cognitive Services Data Reader (Preview)'` + - `'Cognitive Services Face Recognizer'` + - `'Cognitive Services Immersive Reader User'` + - `'Cognitive Services Language Owner'` + - `'Cognitive Services Language Reader'` + - `'Cognitive Services Language Writer'` + - `'Cognitive Services LUIS Owner'` + - `'Cognitive Services LUIS Reader'` + - `'Cognitive Services LUIS Writer'` + - `'Cognitive Services Metrics Advisor Administrator'` + - `'Cognitive Services Metrics Advisor User'` + - `'Cognitive Services OpenAI Contributor'` + - `'Cognitive Services OpenAI User'` + - `'Cognitive Services QnA Maker Editor'` + - `'Cognitive Services QnA Maker Reader'` + - `'Cognitive Services Speech Contributor'` + - `'Cognitive Services Speech User'` + - `'Cognitive Services User'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/communication/communication-service/README.md b/avm/res/communication/communication-service/README.md index def24cd565..e1d2bdd046 100644 --- a/avm/res/communication/communication-service/README.md +++ b/avm/res/communication/communication-service/README.md @@ -606,6 +606,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/communication/email-service/README.md b/avm/res/communication/email-service/README.md index 756b60281b..0087caf5ff 100644 --- a/avm/res/communication/email-service/README.md +++ b/avm/res/communication/email-service/README.md @@ -451,6 +451,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/communication/email-service/domain/README.md b/avm/res/communication/email-service/domain/README.md index 20316013ee..e649034df2 100644 --- a/avm/res/communication/email-service/domain/README.md +++ b/avm/res/communication/email-service/domain/README.md @@ -123,6 +123,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/compute/availability-set/README.md b/avm/res/compute/availability-set/README.md index 57229ca6f3..b058766413 100644 --- a/avm/res/compute/availability-set/README.md +++ b/avm/res/compute/availability-set/README.md @@ -377,6 +377,16 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + - `'Virtual Machine Administrator Login'` + - `'Virtual Machine Contributor'` + - `'Virtual Machine Data Access Administrator (preview)'` + - `'Virtual Machine User Login'` **Required parameters** diff --git a/avm/res/compute/disk-encryption-set/README.md b/avm/res/compute/disk-encryption-set/README.md index eb3e9b657c..85900913fe 100644 --- a/avm/res/compute/disk-encryption-set/README.md +++ b/avm/res/compute/disk-encryption-set/README.md @@ -585,6 +585,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Data Operator for Managed Disks'` + - `'Disk Backup Reader'` + - `'Disk Pool Operator'` + - `'Disk Restore Operator'` + - `'Disk Snapshot Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/compute/disk/README.md b/avm/res/compute/disk/README.md index be1624c4f6..3a95ddb2ad 100644 --- a/avm/res/compute/disk/README.md +++ b/avm/res/compute/disk/README.md @@ -818,6 +818,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Data Operator for Managed Disks'` + - `'Disk Backup Reader'` + - `'Disk Pool Operator'` + - `'Disk Restore Operator'` + - `'Disk Snapshot Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/compute/gallery/README.md b/avm/res/compute/gallery/README.md index 926c7e1b35..62c4b8041f 100644 --- a/avm/res/compute/gallery/README.md +++ b/avm/res/compute/gallery/README.md @@ -1048,6 +1048,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Compute Gallery Sharing Admin'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/compute/gallery/application/README.md b/avm/res/compute/gallery/application/README.md index be70237005..879fb1c1db 100644 --- a/avm/res/compute/gallery/application/README.md +++ b/avm/res/compute/gallery/application/README.md @@ -129,6 +129,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Compute Gallery Sharing Admin'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/compute/gallery/image/README.md b/avm/res/compute/gallery/image/README.md index 98d3d3ec68..484444ae23 100644 --- a/avm/res/compute/gallery/image/README.md +++ b/avm/res/compute/gallery/image/README.md @@ -321,6 +321,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Compute Gallery Sharing Admin'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/compute/image/README.md b/avm/res/compute/image/README.md index 786bf30b97..58545668c5 100644 --- a/avm/res/compute/image/README.md +++ b/avm/res/compute/image/README.md @@ -476,6 +476,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/compute/proximity-placement-group/README.md b/avm/res/compute/proximity-placement-group/README.md index 849182b03d..7bb3c4a857 100644 --- a/avm/res/compute/proximity-placement-group/README.md +++ b/avm/res/compute/proximity-placement-group/README.md @@ -429,6 +429,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/compute/ssh-public-key/README.md b/avm/res/compute/ssh-public-key/README.md index 6db199cbfd..a4ebea4185 100644 --- a/avm/res/compute/ssh-public-key/README.md +++ b/avm/res/compute/ssh-public-key/README.md @@ -403,6 +403,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/compute/virtual-machine-scale-set/README.md b/avm/res/compute/virtual-machine-scale-set/README.md index 730b23f033..febb8bfea7 100644 --- a/avm/res/compute/virtual-machine-scale-set/README.md +++ b/avm/res/compute/virtual-machine-scale-set/README.md @@ -2450,6 +2450,25 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Data Operator for Managed Disks'` + - `'Desktop Virtualization Power On Contributor'` + - `'Desktop Virtualization Power On Off Contributor'` + - `'Desktop Virtualization Virtual Machine Contributor'` + - `'DevTest Labs User'` + - `'Disk Backup Reader'` + - `'Disk Pool Operator'` + - `'Disk Restore Operator'` + - `'Disk Snapshot Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + - `'Virtual Machine Administrator Login'` + - `'Virtual Machine Contributor'` + - `'Virtual Machine User Login'` + - `'VM Scanner Operator'` **Required parameters** diff --git a/avm/res/compute/virtual-machine/README.md b/avm/res/compute/virtual-machine/README.md index f34080a1bc..855f72b4a8 100644 --- a/avm/res/compute/virtual-machine/README.md +++ b/avm/res/compute/virtual-machine/README.md @@ -4210,6 +4210,25 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Data Operator for Managed Disks'` + - `'Desktop Virtualization Power On Contributor'` + - `'Desktop Virtualization Power On Off Contributor'` + - `'Desktop Virtualization Virtual Machine Contributor'` + - `'DevTest Labs User'` + - `'Disk Backup Reader'` + - `'Disk Pool Operator'` + - `'Disk Restore Operator'` + - `'Disk Snapshot Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + - `'Virtual Machine Administrator Login'` + - `'Virtual Machine Contributor'` + - `'Virtual Machine User Login'` + - `'VM Scanner Operator'` **Required parameters** diff --git a/avm/res/container-registry/registry/README.md b/avm/res/container-registry/registry/README.md index f81a37d43a..02594a96ff 100644 --- a/avm/res/container-registry/registry/README.md +++ b/avm/res/container-registry/registry/README.md @@ -1526,6 +1526,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1696,6 +1707,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'AcrDelete'` + - `'AcrImageSigner'` + - `'AcrPull'` + - `'AcrPush'` + - `'AcrQuarantineReader'` + - `'AcrQuarantineWriter'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/container-service/managed-cluster/README.md b/avm/res/container-service/managed-cluster/README.md index 44a7b26a1e..2e988e1ca5 100644 --- a/avm/res/container-service/managed-cluster/README.md +++ b/avm/res/container-service/managed-cluster/README.md @@ -3194,6 +3194,26 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Azure Kubernetes Fleet Manager Contributor Role'` + - `'Azure Kubernetes Fleet Manager RBAC Admin'` + - `'Azure Kubernetes Fleet Manager RBAC Cluster Admin'` + - `'Azure Kubernetes Fleet Manager RBAC Reader'` + - `'Azure Kubernetes Fleet Manager RBAC Writer'` + - `'Azure Kubernetes Service Cluster Admin Role'` + - `'Azure Kubernetes Service Cluster Monitoring User'` + - `'Azure Kubernetes Service Cluster User Role'` + - `'Azure Kubernetes Service Contributor Role'` + - `'Azure Kubernetes Service RBAC Admin'` + - `'Azure Kubernetes Service RBAC Cluster Admin'` + - `'Azure Kubernetes Service RBAC Reader'` + - `'Azure Kubernetes Service RBAC Writer'` + - `'Contributor'` + - `'Kubernetes Agentless Operator'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/data-factory/factory/README.md b/avm/res/data-factory/factory/README.md index d4ee7144b0..bd9ec6e1c6 100644 --- a/avm/res/data-factory/factory/README.md +++ b/avm/res/data-factory/factory/README.md @@ -1270,6 +1270,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1397,6 +1408,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Data Factory Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/data-protection/backup-vault/README.md b/avm/res/data-protection/backup-vault/README.md index 5ef4757a5b..77172ba563 100644 --- a/avm/res/data-protection/backup-vault/README.md +++ b/avm/res/data-protection/backup-vault/README.md @@ -693,6 +693,15 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Backup Contributor'` + - `'Backup Operator'` + - `'Backup Reader'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/databricks/access-connector/README.md b/avm/res/databricks/access-connector/README.md index 5086b63cd6..e825762f75 100644 --- a/avm/res/databricks/access-connector/README.md +++ b/avm/res/databricks/access-connector/README.md @@ -392,6 +392,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/databricks/workspace/README.md b/avm/res/databricks/workspace/README.md index b3084f34a5..5b877cd4ce 100644 --- a/avm/res/databricks/workspace/README.md +++ b/avm/res/databricks/workspace/README.md @@ -1565,6 +1565,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -1977,6 +1983,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/db-for-my-sql/flexible-server/README.md b/avm/res/db-for-my-sql/flexible-server/README.md index f915bedd9d..dbe135fbc5 100644 --- a/avm/res/db-for-my-sql/flexible-server/README.md +++ b/avm/res/db-for-my-sql/flexible-server/README.md @@ -1223,6 +1223,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'MySQL Backup And Export Operator'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/db-for-postgre-sql/flexible-server/README.md b/avm/res/db-for-postgre-sql/flexible-server/README.md index 8e84071338..c60b6026b4 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/README.md +++ b/avm/res/db-for-postgre-sql/flexible-server/README.md @@ -1381,6 +1381,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/desktop-virtualization/application-group/README.md b/avm/res/desktop-virtualization/application-group/README.md index 96040b8018..da9a322e54 100644 --- a/avm/res/desktop-virtualization/application-group/README.md +++ b/avm/res/desktop-virtualization/application-group/README.md @@ -608,6 +608,29 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Owner'` + - `'Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + - `'Application Group Contributor'` + - `'Desktop Virtualization Application Group Contributor'` + - `'Desktop Virtualization Application Group Reader'` + - `'Desktop Virtualization Contributor'` + - `'Desktop Virtualization Host Pool Contributor'` + - `'Desktop Virtualization Host Pool Reader'` + - `'Desktop Virtualization Power On Off Contributor'` + - `'Desktop Virtualization Reader'` + - `'Desktop Virtualization Session Host Operator'` + - `'Desktop Virtualization User'` + - `'Desktop Virtualization User Session Operator'` + - `'Desktop Virtualization Virtual Machine Contributor'` + - `'Desktop Virtualization Workspace Contributor'` + - `'Desktop Virtualization Workspace Reader'` + - `'Managed Application Contributor Role'` + - `'Managed Application Operator Role'` + - `'Managed Applications Reader'` **Required parameters** diff --git a/avm/res/desktop-virtualization/host-pool/README.md b/avm/res/desktop-virtualization/host-pool/README.md index 2d0ab9b957..886a58e81a 100644 --- a/avm/res/desktop-virtualization/host-pool/README.md +++ b/avm/res/desktop-virtualization/host-pool/README.md @@ -1071,6 +1071,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1207,6 +1218,29 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Owner'` + - `'Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + - `'Application Group Contributor'` + - `'Desktop Virtualization Application Group Contributor'` + - `'Desktop Virtualization Application Group Reader'` + - `'Desktop Virtualization Contributor'` + - `'Desktop Virtualization Host Pool Contributor'` + - `'Desktop Virtualization Host Pool Reader'` + - `'Desktop Virtualization Power On Off Contributor'` + - `'Desktop Virtualization Reader'` + - `'Desktop Virtualization Session Host Operator'` + - `'Desktop Virtualization User'` + - `'Desktop Virtualization User Session Operator'` + - `'Desktop Virtualization Virtual Machine Contributor'` + - `'Desktop Virtualization Workspace Contributor'` + - `'Desktop Virtualization Workspace Reader'` + - `'Managed Application Contributor Role'` + - `'Managed Application Operator Role'` + - `'Managed Applications Reader'` **Required parameters** diff --git a/avm/res/desktop-virtualization/scaling-plan/README.md b/avm/res/desktop-virtualization/scaling-plan/README.md index a3860a3213..97207ef73c 100644 --- a/avm/res/desktop-virtualization/scaling-plan/README.md +++ b/avm/res/desktop-virtualization/scaling-plan/README.md @@ -795,6 +795,26 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Owner'` + - `'Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + - `'Application Group Contributor'` + - `'Desktop Virtualization Application Group Contributor'` + - `'Desktop Virtualization Application Group Reader'` + - `'Desktop Virtualization Contributor'` + - `'Desktop Virtualization Host Pool Contributor'` + - `'Desktop Virtualization Host Pool Reader'` + - `'Desktop Virtualization Power On Off Contributor'` + - `'Desktop Virtualization Reader'` + - `'Desktop Virtualization Session Host Operator'` + - `'Desktop Virtualization User'` + - `'Desktop Virtualization User Session Operator'` + - `'Desktop Virtualization Virtual Machine Contributor'` + - `'Desktop Virtualization Workspace Contributor'` + - `'Desktop Virtualization Workspace Reader'` **Required parameters** diff --git a/avm/res/desktop-virtualization/workspace/README.md b/avm/res/desktop-virtualization/workspace/README.md index f37ac6ade8..c71da4bcbb 100644 --- a/avm/res/desktop-virtualization/workspace/README.md +++ b/avm/res/desktop-virtualization/workspace/README.md @@ -971,6 +971,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1090,6 +1101,26 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Owner'` + - `'Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + - `'Application Group Contributor'` + - `'Desktop Virtualization Application Group Contributor'` + - `'Desktop Virtualization Application Group Reader'` + - `'Desktop Virtualization Contributor'` + - `'Desktop Virtualization Host Pool Contributor'` + - `'Desktop Virtualization Host Pool Reader'` + - `'Desktop Virtualization Power On Off Contributor'` + - `'Desktop Virtualization Reader'` + - `'Desktop Virtualization Session Host Operator'` + - `'Desktop Virtualization User'` + - `'Desktop Virtualization User Session Operator'` + - `'Desktop Virtualization Virtual Machine Contributor'` + - `'Desktop Virtualization Workspace Contributor'` + - `'Desktop Virtualization Workspace Reader'` **Required parameters** diff --git a/avm/res/dev-test-lab/lab/README.md b/avm/res/dev-test-lab/lab/README.md index 22a277da2a..4c3db963d8 100644 --- a/avm/res/dev-test-lab/lab/README.md +++ b/avm/res/dev-test-lab/lab/README.md @@ -1590,6 +1590,15 @@ Array of role assignment objects that contain the 'roleDefinitionIdOrName' and ' - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DevTest Labs User'` + - `'Owner'` + - `'Reader'` + - `'Resource Policy Contributor'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + - `'Virtual Machine Contributor'` **Required parameters** diff --git a/avm/res/digital-twins/digital-twins-instance/README.md b/avm/res/digital-twins/digital-twins-instance/README.md index 8dc8fc0cd8..17f002c774 100644 --- a/avm/res/digital-twins/digital-twins-instance/README.md +++ b/avm/res/digital-twins/digital-twins-instance/README.md @@ -996,6 +996,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1115,6 +1126,14 @@ Array of role assignment objects that contain the 'roleDefinitionIdOrName' and ' - Required: No - Type: array +- Roles configurable by name: + - `'Azure Digital Twins Data Owner'` + - `'Azure Digital Twins Data Reader'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/document-db/database-account/README.md b/avm/res/document-db/database-account/README.md index 24654fcc7f..e3b1d30fb6 100644 --- a/avm/res/document-db/database-account/README.md +++ b/avm/res/document-db/database-account/README.md @@ -3189,6 +3189,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -3293,6 +3304,17 @@ Array of role assignment objects that contain the 'roleDefinitionIdOrName' and ' - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Cosmos DB Account Reader Role'` + - `'Cosmos DB Operator'` + - `'CosmosBackupOperator'` + - `'CosmosRestoreOperator'` + - `'DocumentDB Account Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/event-grid/domain/README.md b/avm/res/event-grid/domain/README.md index 7514b32c3d..a9833d9025 100644 --- a/avm/res/event-grid/domain/README.md +++ b/avm/res/event-grid/domain/README.md @@ -1033,6 +1033,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1160,6 +1171,16 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'EventGrid Contributor'` + - `'EventGrid Data Sender'` + - `'EventGrid EventSubscription Contributor'` + - `'EventGrid EventSubscription Reader'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/event-grid/namespace/README.md b/avm/res/event-grid/namespace/README.md index 290a4d6631..7ac321334d 100644 --- a/avm/res/event-grid/namespace/README.md +++ b/avm/res/event-grid/namespace/README.md @@ -2006,6 +2006,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -2132,6 +2143,20 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Azure Resource Notifications System Topics Subscriber'` + - `'Contributor'` + - `'EventGrid Contributor'` + - `'EventGrid Data Contributor'` + - `'EventGrid Data Receiver'` + - `'EventGrid Data Sender'` + - `'EventGrid EventSubscription Contributor'` + - `'EventGrid EventSubscription Reader'` + - `'EventGrid TopicSpaces Publisher'` + - `'EventGrid TopicSpaces Subscriber'` + - `'Owner'` + - `'Reader'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/event-grid/namespace/topic-space/README.md b/avm/res/event-grid/namespace/topic-space/README.md index a931f6ad62..442efe3335 100644 --- a/avm/res/event-grid/namespace/topic-space/README.md +++ b/avm/res/event-grid/namespace/topic-space/README.md @@ -71,6 +71,20 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Azure Resource Notifications System Topics Subscriber'` + - `'Contributor'` + - `'EventGrid Contributor'` + - `'EventGrid Data Contributor'` + - `'EventGrid Data Receiver'` + - `'EventGrid Data Sender'` + - `'EventGrid EventSubscription Contributor'` + - `'EventGrid EventSubscription Reader'` + - `'EventGrid TopicSpaces Publisher'` + - `'EventGrid TopicSpaces Subscriber'` + - `'Owner'` + - `'Reader'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/event-grid/namespace/topic/README.md b/avm/res/event-grid/namespace/topic/README.md index 8b0920ea45..9d831de41c 100644 --- a/avm/res/event-grid/namespace/topic/README.md +++ b/avm/res/event-grid/namespace/topic/README.md @@ -129,6 +129,20 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Azure Resource Notifications System Topics Subscriber'` + - `'Contributor'` + - `'EventGrid Contributor'` + - `'EventGrid Data Contributor'` + - `'EventGrid Data Receiver'` + - `'EventGrid Data Sender'` + - `'EventGrid EventSubscription Contributor'` + - `'EventGrid EventSubscription Reader'` + - `'EventGrid TopicSpaces Publisher'` + - `'EventGrid TopicSpaces Subscriber'` + - `'Owner'` + - `'Reader'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/event-grid/namespace/topic/event-subscription/README.md b/avm/res/event-grid/namespace/topic/event-subscription/README.md index 8b2fcd5527..3efe652422 100644 --- a/avm/res/event-grid/namespace/topic/event-subscription/README.md +++ b/avm/res/event-grid/namespace/topic/event-subscription/README.md @@ -88,6 +88,20 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Azure Resource Notifications System Topics Subscriber'` + - `'Contributor'` + - `'EventGrid Contributor'` + - `'EventGrid Data Contributor'` + - `'EventGrid Data Receiver'` + - `'EventGrid Data Sender'` + - `'EventGrid EventSubscription Contributor'` + - `'EventGrid EventSubscription Reader'` + - `'EventGrid TopicSpaces Publisher'` + - `'EventGrid TopicSpaces Subscriber'` + - `'Owner'` + - `'Reader'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/event-grid/system-topic/README.md b/avm/res/event-grid/system-topic/README.md index eb74e20320..98c9a44742 100644 --- a/avm/res/event-grid/system-topic/README.md +++ b/avm/res/event-grid/system-topic/README.md @@ -732,6 +732,16 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'EventGrid Contributor'` + - `'EventGrid Data Sender'` + - `'EventGrid EventSubscription Contributor'` + - `'EventGrid EventSubscription Reader'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/event-grid/topic/README.md b/avm/res/event-grid/topic/README.md index 56864ec710..bf85377cfe 100644 --- a/avm/res/event-grid/topic/README.md +++ b/avm/res/event-grid/topic/README.md @@ -1127,6 +1127,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1254,6 +1265,16 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'EventGrid Contributor'` + - `'EventGrid Data Sender'` + - `'EventGrid EventSubscription Contributor'` + - `'EventGrid EventSubscription Reader'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/event-hub/namespace/README.md b/avm/res/event-hub/namespace/README.md index aae733f8da..c32df1eeb0 100644 --- a/avm/res/event-hub/namespace/README.md +++ b/avm/res/event-hub/namespace/README.md @@ -1631,6 +1631,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1767,6 +1778,15 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Azure Event Hubs Data Owner'` + - `'Azure Event Hubs Data Receiver'` + - `'Azure Event Hubs Data Sender'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/event-hub/namespace/eventhub/README.md b/avm/res/event-hub/namespace/eventhub/README.md index 8964602f1b..b10aa5e66f 100644 --- a/avm/res/event-hub/namespace/eventhub/README.md +++ b/avm/res/event-hub/namespace/eventhub/README.md @@ -273,6 +273,15 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Azure Event Hubs Data Owner'` + - `'Azure Event Hubs Data Receiver'` + - `'Azure Event Hubs Data Sender'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/health-bot/health-bot/README.md b/avm/res/health-bot/health-bot/README.md index 032ebd2d90..b23485fd1c 100644 --- a/avm/res/health-bot/health-bot/README.md +++ b/avm/res/health-bot/health-bot/README.md @@ -407,6 +407,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/healthcare-apis/workspace/README.md b/avm/res/healthcare-apis/workspace/README.md index 3347555bd9..992cee3c23 100644 --- a/avm/res/healthcare-apis/workspace/README.md +++ b/avm/res/healthcare-apis/workspace/README.md @@ -590,6 +590,21 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DICOM Data Owner'` + - `'DICOM Data Reader'` + - `'FHIR Data Contributor'` + - `'FHIR Data Converter'` + - `'FHIR Data Exporter'` + - `'FHIR Data Importer'` + - `'FHIR Data Reader'` + - `'FHIR Data Writer'` + - `'FHIR SMART User'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/healthcare-apis/workspace/fhirservice/README.md b/avm/res/healthcare-apis/workspace/fhirservice/README.md index 0835f81584..ce0990cf46 100644 --- a/avm/res/healthcare-apis/workspace/fhirservice/README.md +++ b/avm/res/healthcare-apis/workspace/fhirservice/README.md @@ -468,6 +468,21 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DICOM Data Owner'` + - `'DICOM Data Reader'` + - `'FHIR Data Contributor'` + - `'FHIR Data Converter'` + - `'FHIR Data Exporter'` + - `'FHIR Data Importer'` + - `'FHIR Data Reader'` + - `'FHIR Data Writer'` + - `'FHIR SMART User'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/hybrid-compute/machine/README.md b/avm/res/hybrid-compute/machine/README.md index 5221e3578b..b7f4fd21ba 100644 --- a/avm/res/hybrid-compute/machine/README.md +++ b/avm/res/hybrid-compute/machine/README.md @@ -509,6 +509,16 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + - `'Arc machine Administrator Login'` + - `'Arc machine Contributor'` + - `'Arc machine User Login'` + - `'Windows Admin Center Administrator Login'` **Required parameters** diff --git a/avm/res/insights/action-group/README.md b/avm/res/insights/action-group/README.md index 5ca2f832b8..becc4cda4d 100644 --- a/avm/res/insights/action-group/README.md +++ b/avm/res/insights/action-group/README.md @@ -413,6 +413,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/insights/activity-log-alert/README.md b/avm/res/insights/activity-log-alert/README.md index ab8e82e305..3dc67e6cf2 100644 --- a/avm/res/insights/activity-log-alert/README.md +++ b/avm/res/insights/activity-log-alert/README.md @@ -553,6 +553,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/insights/component/README.md b/avm/res/insights/component/README.md index 458098b208..e4f7b78ac5 100644 --- a/avm/res/insights/component/README.md +++ b/avm/res/insights/component/README.md @@ -645,6 +645,16 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + - `'Monitoring Metrics Publisher'` + - `'Application Insights Component Contributor'` + - `'Application Insights Snapshot Debugger'` + - `'Monitoring Contributor'` **Required parameters** diff --git a/avm/res/insights/data-collection-endpoint/README.md b/avm/res/insights/data-collection-endpoint/README.md index a1cd20427a..a2f09497c1 100644 --- a/avm/res/insights/data-collection-endpoint/README.md +++ b/avm/res/insights/data-collection-endpoint/README.md @@ -391,6 +391,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/insights/data-collection-rule/README.md b/avm/res/insights/data-collection-rule/README.md index 6134656d6d..300de97955 100644 --- a/avm/res/insights/data-collection-rule/README.md +++ b/avm/res/insights/data-collection-rule/README.md @@ -2052,6 +2052,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/insights/metric-alert/README.md b/avm/res/insights/metric-alert/README.md index a28a379fae..121b127c85 100644 --- a/avm/res/insights/metric-alert/README.md +++ b/avm/res/insights/metric-alert/README.md @@ -498,6 +498,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/insights/private-link-scope/README.md b/avm/res/insights/private-link-scope/README.md index 9c6f5d5719..4dda77b209 100644 --- a/avm/res/insights/private-link-scope/README.md +++ b/avm/res/insights/private-link-scope/README.md @@ -1221,6 +1221,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1332,6 +1343,21 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Log Analytics Contributor'` + - `'Log Analytics Reader'` + - `'Logic App Contributor'` + - `'Monitoring Contributor'` + - `'Monitoring Metrics Publisher'` + - `'Monitoring Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'Tag Contributor'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/insights/scheduled-query-rule/README.md b/avm/res/insights/scheduled-query-rule/README.md index c0f4c7a979..7497372fa6 100644 --- a/avm/res/insights/scheduled-query-rule/README.md +++ b/avm/res/insights/scheduled-query-rule/README.md @@ -658,6 +658,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/insights/webtest/README.md b/avm/res/insights/webtest/README.md index 2b0ca9def0..ef3bac6253 100644 --- a/avm/res/insights/webtest/README.md +++ b/avm/res/insights/webtest/README.md @@ -529,6 +529,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/key-vault/vault/README.md b/avm/res/key-vault/vault/README.md index 10326e55cc..9997df4ed0 100644 --- a/avm/res/key-vault/vault/README.md +++ b/avm/res/key-vault/vault/README.md @@ -1681,6 +1681,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Key Vault Administrator'` + - `'Key Vault Contributor'` + - `'Key Vault Crypto Officer'` + - `'Key Vault Crypto Service Encryption User'` + - `'Key Vault Crypto User'` + - `'Key Vault Reader'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -2223,6 +2235,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -2350,6 +2373,22 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Key Vault Administrator'` + - `'Key Vault Certificates Officer'` + - `'Key Vault Certificate User'` + - `'Key Vault Contributor'` + - `'Key Vault Crypto Officer'` + - `'Key Vault Crypto Service Encryption User'` + - `'Key Vault Crypto User'` + - `'Key Vault Reader'` + - `'Key Vault Secrets Officer'` + - `'Key Vault Secrets User'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -2527,6 +2566,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Key Vault Administrator'` + - `'Key Vault Contributor'` + - `'Key Vault Reader'` + - `'Key Vault Secrets Officer'` + - `'Key Vault Secrets User'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/key-vault/vault/key/README.md b/avm/res/key-vault/vault/key/README.md index 4531fa3eab..a5dfe17fe2 100644 --- a/avm/res/key-vault/vault/key/README.md +++ b/avm/res/key-vault/vault/key/README.md @@ -154,6 +154,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Key Vault Administrator'` + - `'Key Vault Contributor'` + - `'Key Vault Crypto Officer'` + - `'Key Vault Crypto Service Encryption User'` + - `'Key Vault Crypto User'` + - `'Key Vault Reader'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/key-vault/vault/secret/README.md b/avm/res/key-vault/vault/secret/README.md index d62aac35dd..b05c38071f 100644 --- a/avm/res/key-vault/vault/secret/README.md +++ b/avm/res/key-vault/vault/secret/README.md @@ -97,6 +97,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Key Vault Administrator'` + - `'Key Vault Contributor'` + - `'Key Vault Reader'` + - `'Key Vault Secrets Officer'` + - `'Key Vault Secrets User'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/kusto/cluster/README.md b/avm/res/kusto/cluster/README.md index 8d94f1f46f..40d7a0e115 100644 --- a/avm/res/kusto/cluster/README.md +++ b/avm/res/kusto/cluster/README.md @@ -1390,6 +1390,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1510,6 +1521,10 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` **Required parameters** diff --git a/avm/res/load-test-service/load-test/README.md b/avm/res/load-test-service/load-test/README.md index 16936f9cdf..687a4e16a1 100644 --- a/avm/res/load-test-service/load-test/README.md +++ b/avm/res/load-test-service/load-test/README.md @@ -509,6 +509,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/logic/workflow/README.md b/avm/res/logic/workflow/README.md index fc08456b5c..a3e33ad480 100644 --- a/avm/res/logic/workflow/README.md +++ b/avm/res/logic/workflow/README.md @@ -805,6 +805,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Logic App Contributor'` + - `'Logic App Operator'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/machine-learning-services/workspace/README.md b/avm/res/machine-learning-services/workspace/README.md index 860f066e50..e8b2aa23b5 100644 --- a/avm/res/machine-learning-services/workspace/README.md +++ b/avm/res/machine-learning-services/workspace/README.md @@ -2035,6 +2035,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -2161,6 +2172,16 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'AzureML Compute Operator'` + - `'AzureML Data Scientist'` + - `'AzureML Metrics Writer (preview)'` + - `'AzureML Registry User'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/maintenance/maintenance-configuration/README.md b/avm/res/maintenance/maintenance-configuration/README.md index b2fe74ff0c..d8187c2a1f 100644 --- a/avm/res/maintenance/maintenance-configuration/README.md +++ b/avm/res/maintenance/maintenance-configuration/README.md @@ -528,6 +528,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'Scheduled Patching Contributor'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/managed-identity/user-assigned-identity/README.md b/avm/res/managed-identity/user-assigned-identity/README.md index 6ab0ca2082..43908d19fe 100644 --- a/avm/res/managed-identity/user-assigned-identity/README.md +++ b/avm/res/managed-identity/user-assigned-identity/README.md @@ -463,6 +463,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Managed Identity Contributor'` + - `'Managed Identity Operator'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/net-app/net-app-account/README.md b/avm/res/net-app/net-app-account/README.md index 7ee4003cbe..13b76bd308 100644 --- a/avm/res/net-app/net-app-account/README.md +++ b/avm/res/net-app/net-app-account/README.md @@ -951,6 +951,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/net-app/net-app-account/capacity-pool/README.md b/avm/res/net-app/net-app-account/capacity-pool/README.md index e8d582aa36..4c95a9ca0b 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/README.md +++ b/avm/res/net-app/net-app-account/capacity-pool/README.md @@ -130,6 +130,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/net-app/net-app-account/capacity-pool/volume/README.md b/avm/res/net-app/net-app-account/capacity-pool/volume/README.md index f7d5472e35..9361b0045e 100644 --- a/avm/res/net-app/net-app-account/capacity-pool/volume/README.md +++ b/avm/res/net-app/net-app-account/capacity-pool/volume/README.md @@ -399,6 +399,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/application-gateway/README.md b/avm/res/network/application-gateway/README.md index b5d206d002..ad061f01d1 100644 --- a/avm/res/network/application-gateway/README.md +++ b/avm/res/network/application-gateway/README.md @@ -2840,6 +2840,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -2984,6 +2995,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/application-security-group/README.md b/avm/res/network/application-security-group/README.md index 227c71bc93..6ef90cc95a 100644 --- a/avm/res/network/application-security-group/README.md +++ b/avm/res/network/application-security-group/README.md @@ -341,6 +341,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/azure-firewall/README.md b/avm/res/network/azure-firewall/README.md index cb38f1c505..ce6a6114ef 100644 --- a/avm/res/network/azure-firewall/README.md +++ b/avm/res/network/azure-firewall/README.md @@ -2079,6 +2079,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/bastion-host/README.md b/avm/res/network/bastion-host/README.md index a7c2f5dd91..fc93544fc5 100644 --- a/avm/res/network/bastion-host/README.md +++ b/avm/res/network/bastion-host/README.md @@ -764,6 +764,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/ddos-protection-plan/README.md b/avm/res/network/ddos-protection-plan/README.md index 5d9187c4d3..6bdd5c6b3c 100644 --- a/avm/res/network/ddos-protection-plan/README.md +++ b/avm/res/network/ddos-protection-plan/README.md @@ -341,6 +341,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/dns-forwarding-ruleset/README.md b/avm/res/network/dns-forwarding-ruleset/README.md index d126a5c2a5..e0d4cf14ae 100644 --- a/avm/res/network/dns-forwarding-ruleset/README.md +++ b/avm/res/network/dns-forwarding-ruleset/README.md @@ -504,6 +504,15 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` **Required parameters** diff --git a/avm/res/network/dns-resolver/README.md b/avm/res/network/dns-resolver/README.md index 8437207f1f..4642dd8e31 100644 --- a/avm/res/network/dns-resolver/README.md +++ b/avm/res/network/dns-resolver/README.md @@ -542,6 +542,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` **Required parameters** diff --git a/avm/res/network/dns-zone/README.md b/avm/res/network/dns-zone/README.md index 8a4e661ec1..e7a9c463a3 100644 --- a/avm/res/network/dns-zone/README.md +++ b/avm/res/network/dns-zone/README.md @@ -940,6 +940,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -1108,6 +1120,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -1291,6 +1315,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -1452,6 +1488,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -1679,6 +1727,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -1839,6 +1899,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -1999,6 +2071,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -2103,6 +2187,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -2236,6 +2332,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -2444,6 +2552,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -2635,6 +2755,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/dns-zone/a/README.md b/avm/res/network/dns-zone/a/README.md index 8d42ef0747..e93a99ac4a 100644 --- a/avm/res/network/dns-zone/a/README.md +++ b/avm/res/network/dns-zone/a/README.md @@ -73,6 +73,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/dns-zone/aaaa/README.md b/avm/res/network/dns-zone/aaaa/README.md index 93755b92fc..99120461e1 100644 --- a/avm/res/network/dns-zone/aaaa/README.md +++ b/avm/res/network/dns-zone/aaaa/README.md @@ -73,6 +73,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/dns-zone/caa/README.md b/avm/res/network/dns-zone/caa/README.md index 8b433084d9..7d1b1ef225 100644 --- a/avm/res/network/dns-zone/caa/README.md +++ b/avm/res/network/dns-zone/caa/README.md @@ -72,6 +72,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/dns-zone/cname/README.md b/avm/res/network/dns-zone/cname/README.md index 197a33a7fd..7107700e87 100644 --- a/avm/res/network/dns-zone/cname/README.md +++ b/avm/res/network/dns-zone/cname/README.md @@ -73,6 +73,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/dns-zone/mx/README.md b/avm/res/network/dns-zone/mx/README.md index 19577e558e..68615af23c 100644 --- a/avm/res/network/dns-zone/mx/README.md +++ b/avm/res/network/dns-zone/mx/README.md @@ -72,6 +72,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/dns-zone/ns/README.md b/avm/res/network/dns-zone/ns/README.md index bf63641c45..43fd817fe2 100644 --- a/avm/res/network/dns-zone/ns/README.md +++ b/avm/res/network/dns-zone/ns/README.md @@ -72,6 +72,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/dns-zone/ptr/README.md b/avm/res/network/dns-zone/ptr/README.md index d5fffa162b..2dda8e4f59 100644 --- a/avm/res/network/dns-zone/ptr/README.md +++ b/avm/res/network/dns-zone/ptr/README.md @@ -72,6 +72,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/dns-zone/soa/README.md b/avm/res/network/dns-zone/soa/README.md index 3c0227fea8..22832138fb 100644 --- a/avm/res/network/dns-zone/soa/README.md +++ b/avm/res/network/dns-zone/soa/README.md @@ -65,6 +65,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/dns-zone/srv/README.md b/avm/res/network/dns-zone/srv/README.md index 309a786101..c733d69571 100644 --- a/avm/res/network/dns-zone/srv/README.md +++ b/avm/res/network/dns-zone/srv/README.md @@ -65,6 +65,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/dns-zone/txt/README.md b/avm/res/network/dns-zone/txt/README.md index dbc21c21cd..35f4be5c49 100644 --- a/avm/res/network/dns-zone/txt/README.md +++ b/avm/res/network/dns-zone/txt/README.md @@ -65,6 +65,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/express-route-circuit/README.md b/avm/res/network/express-route-circuit/README.md index 578169e9ff..50e00e3bd5 100644 --- a/avm/res/network/express-route-circuit/README.md +++ b/avm/res/network/express-route-circuit/README.md @@ -717,6 +717,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/express-route-gateway/README.md b/avm/res/network/express-route-gateway/README.md index cba6d19ddf..d66a1ef172 100644 --- a/avm/res/network/express-route-gateway/README.md +++ b/avm/res/network/express-route-gateway/README.md @@ -409,6 +409,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/front-door-web-application-firewall-policy/README.md b/avm/res/network/front-door-web-application-firewall-policy/README.md index 02c147d79e..6b4b2dd0df 100644 --- a/avm/res/network/front-door-web-application-firewall-policy/README.md +++ b/avm/res/network/front-door-web-application-firewall-policy/README.md @@ -675,6 +675,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/front-door/README.md b/avm/res/network/front-door/README.md index 1fc4d43721..50e01165f6 100644 --- a/avm/res/network/front-door/README.md +++ b/avm/res/network/front-door/README.md @@ -1157,6 +1157,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/ip-group/README.md b/avm/res/network/ip-group/README.md index 211d8de421..48dcd81c36 100644 --- a/avm/res/network/ip-group/README.md +++ b/avm/res/network/ip-group/README.md @@ -360,6 +360,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'IPAM Pool Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` **Required parameters** diff --git a/avm/res/network/load-balancer/README.md b/avm/res/network/load-balancer/README.md index 2ca817beb0..4b5103926d 100644 --- a/avm/res/network/load-balancer/README.md +++ b/avm/res/network/load-balancer/README.md @@ -1459,6 +1459,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/local-network-gateway/README.md b/avm/res/network/local-network-gateway/README.md index 0e702b10ed..aa324dae8a 100644 --- a/avm/res/network/local-network-gateway/README.md +++ b/avm/res/network/local-network-gateway/README.md @@ -445,6 +445,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/nat-gateway/README.md b/avm/res/network/nat-gateway/README.md index ed01e1d452..8c6358ff0c 100644 --- a/avm/res/network/nat-gateway/README.md +++ b/avm/res/network/nat-gateway/README.md @@ -628,6 +628,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/network-interface/README.md b/avm/res/network/network-interface/README.md index 3e9767ba44..6b6f123af8 100644 --- a/avm/res/network/network-interface/README.md +++ b/avm/res/network/network-interface/README.md @@ -722,6 +722,15 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` **Required parameters** diff --git a/avm/res/network/network-manager/README.md b/avm/res/network/network-manager/README.md index 99f4273940..ea6a3831c0 100644 --- a/avm/res/network/network-manager/README.md +++ b/avm/res/network/network-manager/README.md @@ -1075,6 +1075,16 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'IPAM Pool Contributor'` + - `'LocalNGFirewallAdministrator role'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Resource Policy Contributor'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/network-security-group/README.md b/avm/res/network/network-security-group/README.md index 15b057b8ce..3de82ead53 100644 --- a/avm/res/network/network-security-group/README.md +++ b/avm/res/network/network-security-group/README.md @@ -693,6 +693,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/network-watcher/README.md b/avm/res/network/network-watcher/README.md index a13c4ae45b..d26f9648ba 100644 --- a/avm/res/network/network-watcher/README.md +++ b/avm/res/network/network-watcher/README.md @@ -605,6 +605,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/private-dns-zone/README.md b/avm/res/network/private-dns-zone/README.md index b9bcac8c5e..bcc558ab55 100644 --- a/avm/res/network/private-dns-zone/README.md +++ b/avm/res/network/private-dns-zone/README.md @@ -823,6 +823,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Private DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -983,6 +991,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Private DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -1143,6 +1159,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Private DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -1363,6 +1387,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Private DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -1523,6 +1555,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Private DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -1627,6 +1667,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` **Required parameters** @@ -1760,6 +1807,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Private DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -1968,6 +2023,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Private DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -2159,6 +2222,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Private DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/private-dns-zone/a/README.md b/avm/res/network/private-dns-zone/a/README.md index 1d776a8b07..6584b4966c 100644 --- a/avm/res/network/private-dns-zone/a/README.md +++ b/avm/res/network/private-dns-zone/a/README.md @@ -72,6 +72,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Private DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/private-dns-zone/aaaa/README.md b/avm/res/network/private-dns-zone/aaaa/README.md index 8eb2f75a00..01b9ca1fbe 100644 --- a/avm/res/network/private-dns-zone/aaaa/README.md +++ b/avm/res/network/private-dns-zone/aaaa/README.md @@ -72,6 +72,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Private DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/private-dns-zone/cname/README.md b/avm/res/network/private-dns-zone/cname/README.md index 0e3cba5ca8..bcabc2f0a8 100644 --- a/avm/res/network/private-dns-zone/cname/README.md +++ b/avm/res/network/private-dns-zone/cname/README.md @@ -72,6 +72,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Private DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/private-dns-zone/mx/README.md b/avm/res/network/private-dns-zone/mx/README.md index 3284c3e1e7..721b698ccd 100644 --- a/avm/res/network/private-dns-zone/mx/README.md +++ b/avm/res/network/private-dns-zone/mx/README.md @@ -72,6 +72,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Private DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/private-dns-zone/ptr/README.md b/avm/res/network/private-dns-zone/ptr/README.md index 23549d6a1f..0c8d412a53 100644 --- a/avm/res/network/private-dns-zone/ptr/README.md +++ b/avm/res/network/private-dns-zone/ptr/README.md @@ -72,6 +72,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Private DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/private-dns-zone/soa/README.md b/avm/res/network/private-dns-zone/soa/README.md index 5936d5e83c..fd6c40a7fb 100644 --- a/avm/res/network/private-dns-zone/soa/README.md +++ b/avm/res/network/private-dns-zone/soa/README.md @@ -65,6 +65,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Private DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/private-dns-zone/srv/README.md b/avm/res/network/private-dns-zone/srv/README.md index 01a7f68037..a6047c6bda 100644 --- a/avm/res/network/private-dns-zone/srv/README.md +++ b/avm/res/network/private-dns-zone/srv/README.md @@ -65,6 +65,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Private DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/private-dns-zone/txt/README.md b/avm/res/network/private-dns-zone/txt/README.md index 5439d372f2..2121b15b16 100644 --- a/avm/res/network/private-dns-zone/txt/README.md +++ b/avm/res/network/private-dns-zone/txt/README.md @@ -65,6 +65,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Private DNS Zone Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/private-endpoint/README.md b/avm/res/network/private-endpoint/README.md index 9aa9d1085e..c063742796 100644 --- a/avm/res/network/private-endpoint/README.md +++ b/avm/res/network/private-endpoint/README.md @@ -942,6 +942,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` **Required parameters** diff --git a/avm/res/network/private-link-service/README.md b/avm/res/network/private-link-service/README.md index 1cf7759826..3df921a3c3 100644 --- a/avm/res/network/private-link-service/README.md +++ b/avm/res/network/private-link-service/README.md @@ -570,6 +570,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/public-ip-address/README.md b/avm/res/network/public-ip-address/README.md index 9f4526d01e..6c71fc4c87 100644 --- a/avm/res/network/public-ip-address/README.md +++ b/avm/res/network/public-ip-address/README.md @@ -810,6 +810,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator'` **Required parameters** diff --git a/avm/res/network/public-ip-prefix/README.md b/avm/res/network/public-ip-prefix/README.md index 8b70c24c45..22a3790bf4 100644 --- a/avm/res/network/public-ip-prefix/README.md +++ b/avm/res/network/public-ip-prefix/README.md @@ -371,6 +371,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/route-table/README.md b/avm/res/network/route-table/README.md index c93c63b58a..a0a83836fb 100644 --- a/avm/res/network/route-table/README.md +++ b/avm/res/network/route-table/README.md @@ -395,6 +395,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/service-endpoint-policy/README.md b/avm/res/network/service-endpoint-policy/README.md index bc95b35f17..7d1097c4b2 100644 --- a/avm/res/network/service-endpoint-policy/README.md +++ b/avm/res/network/service-endpoint-policy/README.md @@ -240,6 +240,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/trafficmanagerprofile/README.md b/avm/res/network/trafficmanagerprofile/README.md index d7f8192264..c7dfa7d65c 100644 --- a/avm/res/network/trafficmanagerprofile/README.md +++ b/avm/res/network/trafficmanagerprofile/README.md @@ -675,6 +675,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` + - `'Traffic Manager Contributor'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/virtual-network-gateway/README.md b/avm/res/network/virtual-network-gateway/README.md index 36648bf55a..83cde965b0 100644 --- a/avm/res/network/virtual-network-gateway/README.md +++ b/avm/res/network/virtual-network-gateway/README.md @@ -1582,6 +1582,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/virtual-network/README.md b/avm/res/network/virtual-network/README.md index 4b6fa95d30..b3f52a10bb 100644 --- a/avm/res/network/virtual-network/README.md +++ b/avm/res/network/virtual-network/README.md @@ -1195,6 +1195,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -1415,6 +1422,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/virtual-network/subnet/README.md b/avm/res/network/virtual-network/subnet/README.md index e6269f8046..9a975d39da 100644 --- a/avm/res/network/virtual-network/subnet/README.md +++ b/avm/res/network/virtual-network/subnet/README.md @@ -144,6 +144,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/virtual-wan/README.md b/avm/res/network/virtual-wan/README.md index 18b3f54a3b..418464fd84 100644 --- a/avm/res/network/virtual-wan/README.md +++ b/avm/res/network/virtual-wan/README.md @@ -391,6 +391,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/network/vpn-site/README.md b/avm/res/network/vpn-site/README.md index 07e306d08b..63156245bb 100644 --- a/avm/res/network/vpn-site/README.md +++ b/avm/res/network/vpn-site/README.md @@ -606,6 +606,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Network Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/operational-insights/workspace/README.md b/avm/res/operational-insights/workspace/README.md index 2290266bfb..59a1b53a9a 100644 --- a/avm/res/operational-insights/workspace/README.md +++ b/avm/res/operational-insights/workspace/README.md @@ -2081,6 +2081,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Log Analytics Contributor'` + - `'Log Analytics Reader'` + - `'Monitoring Contributor'` + - `'Monitoring Reader'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'Security Admin'` + - `'Security Reader'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/operational-insights/workspace/table/README.md b/avm/res/operational-insights/workspace/table/README.md index 7313d497ee..dc20f643c0 100644 --- a/avm/res/operational-insights/workspace/table/README.md +++ b/avm/res/operational-insights/workspace/table/README.md @@ -92,6 +92,16 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Log Analytics Contributor'` + - `'Log Analytics Reader'` + - `'Monitoring Contributor'` + - `'Monitoring Reader'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/portal/dashboard/README.md b/avm/res/portal/dashboard/README.md index 0a33b644ff..07b61aa261 100644 --- a/avm/res/portal/dashboard/README.md +++ b/avm/res/portal/dashboard/README.md @@ -796,6 +796,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/power-bi-dedicated/capacity/README.md b/avm/res/power-bi-dedicated/capacity/README.md index 0ef1e8e8f4..b01901daba 100644 --- a/avm/res/power-bi-dedicated/capacity/README.md +++ b/avm/res/power-bi-dedicated/capacity/README.md @@ -478,6 +478,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Log Analytics Contributor'` + - `'Log Analytics Reader'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/purview/account/README.md b/avm/res/purview/account/README.md index 26ccfdb7d2..cc71dad4f6 100644 --- a/avm/res/purview/account/README.md +++ b/avm/res/purview/account/README.md @@ -2165,6 +2165,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -2546,6 +2552,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -2941,6 +2953,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/recovery-services/vault/README.md b/avm/res/recovery-services/vault/README.md index b6ca61f7e5..06c21b10e9 100644 --- a/avm/res/recovery-services/vault/README.md +++ b/avm/res/recovery-services/vault/README.md @@ -2445,6 +2445,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -2603,6 +2614,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Backup Contributor'` + - `'Backup Operator'` + - `'Backup Reader'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'Site Recovery Contributor'` + - `'Site Recovery Operator'` + - `'Site Recovery Reader'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/relay/namespace/README.md b/avm/res/relay/namespace/README.md index 837fa1fa43..06c2c905d1 100644 --- a/avm/res/relay/namespace/README.md +++ b/avm/res/relay/namespace/README.md @@ -1215,6 +1215,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1326,6 +1337,15 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Azure Relay Listener'` + - `'Azure Relay Owner'` + - `'Azure Relay Sender'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/relay/namespace/hybrid-connection/README.md b/avm/res/relay/namespace/hybrid-connection/README.md index 96212178af..8df88f210d 100644 --- a/avm/res/relay/namespace/hybrid-connection/README.md +++ b/avm/res/relay/namespace/hybrid-connection/README.md @@ -144,6 +144,15 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Azure Relay Listener'` + - `'Azure Relay Owner'` + - `'Azure Relay Sender'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/relay/namespace/wcf-relay/README.md b/avm/res/relay/namespace/wcf-relay/README.md index abc0f0c610..80caef4d77 100644 --- a/avm/res/relay/namespace/wcf-relay/README.md +++ b/avm/res/relay/namespace/wcf-relay/README.md @@ -161,6 +161,15 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Azure Relay Listener'` + - `'Azure Relay Owner'` + - `'Azure Relay Sender'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/resource-graph/query/README.md b/avm/res/resource-graph/query/README.md index fd64204c24..316e8d7090 100644 --- a/avm/res/resource-graph/query/README.md +++ b/avm/res/resource-graph/query/README.md @@ -374,6 +374,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/resources/deployment-script/README.md b/avm/res/resources/deployment-script/README.md index 9dac2f308c..a922e8496c 100644 --- a/avm/res/resources/deployment-script/README.md +++ b/avm/res/resources/deployment-script/README.md @@ -1012,6 +1012,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/search/search-service/README.md b/avm/res/search/search-service/README.md index a0de84e740..9001f0359c 100644 --- a/avm/res/search/search-service/README.md +++ b/avm/res/search/search-service/README.md @@ -1236,6 +1236,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1370,6 +1381,15 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'Search Index Data Contributor'` + - `'Search Index Data Reader'` + - `'Search Service Contributor'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/service-bus/namespace/README.md b/avm/res/service-bus/namespace/README.md index 0e05ec12f1..178f3dd665 100644 --- a/avm/res/service-bus/namespace/README.md +++ b/avm/res/service-bus/namespace/README.md @@ -2000,6 +2000,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -2354,6 +2365,15 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Azure Service Bus Data Owner'` + - `'Azure Service Bus Data Receiver'` + - `'Azure Service Bus Data Sender'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -2480,6 +2500,15 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Azure Service Bus Data Owner'` + - `'Azure Service Bus Data Receiver'` + - `'Azure Service Bus Data Sender'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** @@ -2764,6 +2793,15 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Azure Service Bus Data Owner'` + - `'Azure Service Bus Data Receiver'` + - `'Azure Service Bus Data Sender'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/service-bus/namespace/queue/README.md b/avm/res/service-bus/namespace/queue/README.md index cc4a9b560c..0c581d4e73 100644 --- a/avm/res/service-bus/namespace/queue/README.md +++ b/avm/res/service-bus/namespace/queue/README.md @@ -236,6 +236,15 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Azure Service Bus Data Owner'` + - `'Azure Service Bus Data Receiver'` + - `'Azure Service Bus Data Sender'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/service-bus/namespace/topic/README.md b/avm/res/service-bus/namespace/topic/README.md index 59c7b43e97..7362055d9e 100644 --- a/avm/res/service-bus/namespace/topic/README.md +++ b/avm/res/service-bus/namespace/topic/README.md @@ -201,6 +201,15 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Azure Service Bus Data Owner'` + - `'Azure Service Bus Data Receiver'` + - `'Azure Service Bus Data Sender'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/service-fabric/cluster/README.md b/avm/res/service-fabric/cluster/README.md index bdeeb3417c..08c89d43fa 100644 --- a/avm/res/service-fabric/cluster/README.md +++ b/avm/res/service-fabric/cluster/README.md @@ -1339,6 +1339,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/signal-r-service/signal-r/README.md b/avm/res/signal-r-service/signal-r/README.md index 35f25de4aa..9d3688a0d3 100644 --- a/avm/res/signal-r-service/signal-r/README.md +++ b/avm/res/signal-r-service/signal-r/README.md @@ -983,6 +983,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1129,6 +1140,20 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'SignalR AccessKey Reader'` + - `'SignalR App Server'` + - `'SignalR REST API Owner'` + - `'SignalR REST API Reader'` + - `'SignalR Service Owner'` + - `'SignalR/Web PubSub Contributor'` + - `'User Access Administrator'` + - `'Web PubSub Service Owner (Preview)'` + - `'Web PubSub Service Reader (Preview)'` **Required parameters** diff --git a/avm/res/signal-r-service/web-pub-sub/README.md b/avm/res/signal-r-service/web-pub-sub/README.md index 46fb8b2483..e255f3e70c 100644 --- a/avm/res/signal-r-service/web-pub-sub/README.md +++ b/avm/res/signal-r-service/web-pub-sub/README.md @@ -952,6 +952,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1098,6 +1109,20 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'SignalR AccessKey Reader'` + - `'SignalR App Server'` + - `'SignalR REST API Owner'` + - `'SignalR REST API Reader'` + - `'SignalR Service Owner'` + - `'SignalR/Web PubSub Contributor'` + - `'User Access Administrator'` + - `'Web PubSub Service Owner (Preview)'` + - `'Web PubSub Service Reader (Preview)'` **Required parameters** diff --git a/avm/res/sql/managed-instance/README.md b/avm/res/sql/managed-instance/README.md index a9394f7cf0..4efc05d72d 100644 --- a/avm/res/sql/managed-instance/README.md +++ b/avm/res/sql/managed-instance/README.md @@ -1286,6 +1286,19 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Reservation Purchaser'` + - `'Role Based Access Control Administrator (Preview)'` + - `'SQL DB Contributor'` + - `'SQL Managed Instance Contributor'` + - `'SQL Security Manager'` + - `'SQL Server Contributor'` + - `'SqlDb Migration Role'` + - `'SqlMI Migration Role'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/sql/server/README.md b/avm/res/sql/server/README.md index e4306982f2..4f73df2fa8 100644 --- a/avm/res/sql/server/README.md +++ b/avm/res/sql/server/README.md @@ -1722,6 +1722,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1865,6 +1876,19 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Reservation Purchaser'` + - `'Role Based Access Control Administrator'` + - `'SQL DB Contributor'` + - `'SQL Managed Instance Contributor'` + - `'SQL Security Manager'` + - `'SQL Server Contributor'` + - `'SqlDb Migration Role'` + - `'SqlMI Migration Role'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/storage/storage-account/README.md b/avm/res/storage/storage-account/README.md index 557a0ff537..bafe70194f 100644 --- a/avm/res/storage/storage-account/README.md +++ b/avm/res/storage/storage-account/README.md @@ -3134,6 +3134,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -3270,6 +3281,31 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Reader and Data Access'` + - `'Role Based Access Control Administrator'` + - `'Storage Account Backup Contributor'` + - `'Storage Account Contributor'` + - `'Storage Account Key Operator Service Role'` + - `'Storage Blob Data Contributor'` + - `'Storage Blob Data Owner'` + - `'Storage Blob Data Reader'` + - `'Storage Blob Delegator'` + - `'Storage File Data Privileged Contributor'` + - `'Storage File Data Privileged Reader'` + - `'Storage File Data SMB Share Contributor'` + - `'Storage File Data SMB Share Elevated Contributor'` + - `'Storage File Data SMB Share Reader'` + - `'Storage Queue Data Contributor'` + - `'Storage Queue Data Message Processor'` + - `'Storage Queue Data Message Sender'` + - `'Storage Queue Data Reader'` + - `'Storage Table Data Contributor'` + - `'Storage Table Data Reader'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/storage/storage-account/blob-service/container/README.md b/avm/res/storage/storage-account/blob-service/container/README.md index 14d00393ef..c66f943899 100644 --- a/avm/res/storage/storage-account/blob-service/container/README.md +++ b/avm/res/storage/storage-account/blob-service/container/README.md @@ -144,6 +144,20 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Reader and Data Access'` + - `'Role Based Access Control Administrator'` + - `'Storage Account Backup Contributor'` + - `'Storage Account Contributor'` + - `'Storage Account Key Operator Service Role'` + - `'Storage Blob Data Contributor'` + - `'Storage Blob Data Owner'` + - `'Storage Blob Data Reader'` + - `'Storage Blob Delegator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/storage/storage-account/queue-service/queue/README.md b/avm/res/storage/storage-account/queue-service/queue/README.md index 508062614a..ccfbd4b635 100644 --- a/avm/res/storage/storage-account/queue-service/queue/README.md +++ b/avm/res/storage/storage-account/queue-service/queue/README.md @@ -64,6 +64,20 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Reader and Data Access'` + - `'Role Based Access Control Administrator'` + - `'Storage Account Backup Contributor'` + - `'Storage Account Contributor'` + - `'Storage Account Key Operator Service Role'` + - `'Storage Queue Data Contributor'` + - `'Storage Queue Data Message Processor'` + - `'Storage Queue Data Message Sender'` + - `'Storage Queue Data Reader'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/storage/storage-account/table-service/table/README.md b/avm/res/storage/storage-account/table-service/table/README.md index dfe48226f8..4009c666b3 100644 --- a/avm/res/storage/storage-account/table-service/table/README.md +++ b/avm/res/storage/storage-account/table-service/table/README.md @@ -55,6 +55,18 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Reader and Data Access'` + - `'Role Based Access Control Administrator'` + - `'Storage Account Backup Contributor'` + - `'Storage Account Contributor'` + - `'Storage Account Key Operator Service Role'` + - `'Storage Table Data Contributor'` + - `'Storage Table Data Reader'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/synapse/private-link-hub/README.md b/avm/res/synapse/private-link-hub/README.md index 6b406c9bd6..9a2123c05e 100644 --- a/avm/res/synapse/private-link-hub/README.md +++ b/avm/res/synapse/private-link-hub/README.md @@ -708,6 +708,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -819,6 +830,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/synapse/workspace/README.md b/avm/res/synapse/workspace/README.md index d9b6a461a3..45cccee622 100644 --- a/avm/res/synapse/workspace/README.md +++ b/avm/res/synapse/workspace/README.md @@ -1651,6 +1651,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1778,6 +1789,13 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Resource Policy Contributor'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/virtual-machine-images/image-template/README.md b/avm/res/virtual-machine-images/image-template/README.md index 022c56722c..b40afc22f9 100644 --- a/avm/res/virtual-machine-images/image-template/README.md +++ b/avm/res/virtual-machine-images/image-template/README.md @@ -691,6 +691,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/web/connection/README.md b/avm/res/web/connection/README.md index 8c29f78a19..be084c468f 100644 --- a/avm/res/web/connection/README.md +++ b/avm/res/web/connection/README.md @@ -460,6 +460,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/web/hosting-environment/README.md b/avm/res/web/hosting-environment/README.md index c6aa44fb42..568d1fbb7b 100644 --- a/avm/res/web/hosting-environment/README.md +++ b/avm/res/web/hosting-environment/README.md @@ -797,6 +797,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` + - `'User Access Administrator'` **Required parameters** diff --git a/avm/res/web/serverfarm/README.md b/avm/res/web/serverfarm/README.md index 9683316417..0df60a5510 100644 --- a/avm/res/web/serverfarm/README.md +++ b/avm/res/web/serverfarm/README.md @@ -648,6 +648,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + - `'Web Plan Contributor'` + - `'Website Contributor'` **Required parameters** diff --git a/avm/res/web/site/README.md b/avm/res/web/site/README.md index 5b72d460f5..bc9c390cb6 100644 --- a/avm/res/web/site/README.md +++ b/avm/res/web/site/README.md @@ -3001,6 +3001,15 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'App Compliance Automation Administrator'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + - `'Web Plan Contributor'` + - `'Website Contributor'` **Required parameters** diff --git a/avm/res/web/site/slot/README.md b/avm/res/web/site/slot/README.md index 063962c630..135fa084b8 100644 --- a/avm/res/web/site/slot/README.md +++ b/avm/res/web/site/slot/README.md @@ -793,6 +793,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -936,6 +947,15 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'App Compliance Automation Administrator'` + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + - `'Web Plan Contributor'` + - `'Website Contributor'` **Required parameters** diff --git a/avm/res/web/static-site/README.md b/avm/res/web/static-site/README.md index c0b10e3875..e77e8fe0c6 100644 --- a/avm/res/web/static-site/README.md +++ b/avm/res/web/static-site/README.md @@ -940,6 +940,17 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` **Required parameters** @@ -1073,6 +1084,14 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + - `'Web Plan Contributor'` + - `'Website Contributor'` **Required parameters** diff --git a/avm/utilities/pipelines/sharedScripts/Get-NestedResourceList.ps1 b/avm/utilities/pipelines/sharedScripts/Get-NestedResourceList.ps1 index 1f5e5389f2..df38f9eb2f 100644 --- a/avm/utilities/pipelines/sharedScripts/Get-NestedResourceList.ps1 +++ b/avm/utilities/pipelines/sharedScripts/Get-NestedResourceList.ps1 @@ -29,13 +29,20 @@ function Get-NestedResourceList { if ($TemplateFileContent.resources -is [System.Collections.Hashtable]) { # With the introduction of user defined types, a compiled template's resources are not part of an ordered hashtable instead of an array. $currLevelResources += $TemplateFileContent.resources.Keys | ForEach-Object { - $TemplateFileContent.resources[$_] + $elem = $TemplateFileContent.resources[$_] + $elem['identifier'] = $_ + $elem } | Where-Object { $_.existing -ne $true } } else { # Default array - $currLevelResources += $TemplateFileContent.resources + $currLevelResources += $TemplateFileContent.resources | ForEach-Object { + $_['identifier'] = $_.name + $_ + } | Where-Object { + $_.existing -ne $true + } } } foreach ($resource in $currLevelResources) { diff --git a/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 b/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 index 3669c1f75a..09355c6f9d 100644 --- a/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 +++ b/avm/utilities/pipelines/sharedScripts/Set-ModuleReadMe.ps1 @@ -390,7 +390,7 @@ function Set-DefinitionSection { ) } } else { - $formattedDefaultValue = $null + $formattedDefaultValue = $null # Reset value for future iterations } # Format allowed values @@ -420,7 +420,31 @@ function Set-DefinitionSection { ) } } else { - $formattedAllowedValues = $null + $formattedAllowedValues = $null # Reset value for future iterations + } + + # Special case for 'roleAssignments' parameter + if (($parameter.name -eq 'roleAssignments') -and ($TemplateFileContent.variables.keys -contains 'builtInRoleNames')) { + if ([String]::IsNullOrEmpty($ParentName)) { + # Top-level invocation + $roles = $TemplateFileContent.variables.builtInRoleNames.Keys + } else { + # Nested-invocation (requires e.g., roles for of nested private endpoint template) + $flattendResources = Get-NestedResourceList -TemplateFileContent $TemplateFileContent + if ($resourceIdentifier = $flattendResources.identifier | Where-Object { $_ -match "^.*_$ParentName`$" }) { + $roles = ($flattendResources | Where-Object { + $_.identifier -eq $resourceIdentifier + }).properties.template.variables.builtInRoleNames.Keys + } else { + Write-Warning ('Failed to identify roles for parameter [{0}] of type [{1}] as resource with identifier [{2}] was not found in the corresponding linked template.' -f $parameter.name, $ParentName, "*_$ParentName") + } + } + $formattedRoleNames = $roles.count -gt 0 ? @( + '- Roles configurable by name:', + ($roles | ForEach-Object { " - ``'$_'``" } | Out-String).TrimEnd() + ) : $null + } else { + $formattedRoleNames = $null # Reset value for future iterations } # Format example @@ -457,7 +481,8 @@ function Set-DefinitionSection { ('- Type: {0}' -f $type), ((-not [String]::IsNullOrEmpty($formattedDefaultValue)) ? $formattedDefaultValue : $null), ((-not [String]::IsNullOrEmpty($formattedAllowedValues)) ? $formattedAllowedValues : $null), - ((-not [String]::IsNullOrEmpty($formattedExample)) ? $formattedExample : $null) + ((-not [String]::IsNullOrEmpty($formattedRoleNames)) ? $formattedRoleNames : $null), + ((-not [String]::IsNullOrEmpty($formattedExample)) ? $formattedExample : $null), '' ) | Where-Object { $null -ne $_ } From f21032a311672f5f417348870977e63d452cd6ca Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Fri, 13 Sep 2024 17:28:44 +0200 Subject: [PATCH 22/57] feat: Pattern - Azure Image Builder - `avm/ptn/virtual-machine-images/azure-image-builder` (#3005) ## Description This PR introduces the Azure Image Builder pattern to AVM Depends on - #3006 - #3007 - #2992 - #2756 ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.virtual-machine-images.azure-image-builder](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.virtual-machine-images.azure-image-builder.yml/badge.svg?branch=users%2Falsehr%2FaibPattern)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.ptn.virtual-machine-images.azure-image-builder.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --------- Co-authored-by: Maher Aldineh --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + ...ual-machine-images.azure-image-builder.yml | 88 + .../azure-image-builder/README.md | 1159 ++ .../azure-image-builder/main.bicep | 625 + .../azure-image-builder/main.json | 15487 ++++++++++++++++ .../src/icons/AzureComputeGalleries.svg | 181 + .../src/icons/Deployment-Script.png | Bin 0 -> 22186 bytes .../src/icons/ImageTemplates.svg | 227 + .../src/icons/Managed-identities.svg | 31 + .../src/icons/Network-Security-Groups.svg | 9 + .../src/icons/Resource-Groups.svg | 1 + .../src/icons/Storage-Accounts.svg | 22 + .../src/icons/VMImageDefinitions.svg | 196 + .../src/icons/VMImageVersions.svg | 277 + .../src/icons/Virtual-Machine-Scale-Sets.svg | 9 + .../src/icons/Virtual-Networks.svg | 1 + .../src/image/imageBuilderimage.png | Bin 0 -> 211739 bytes .../tests/e2e/defaults/main.test.bicep | 58 + .../tests/e2e/deployAll/main.test.bicep | 136 + .../scripts/Initialize-LinuxSoftware.ps1 | 538 + .../scripts/Initialize-WindowsSoftware.ps1 | 792 + .../scripts/Install-LinuxPowerShell.sh | 44 + .../scripts/Install-WindowsPowerShell.ps1 | 43 + .../dependencies.bicep | 259 + .../deployOnlyAssetsAndImage/main.test.bicep | 90 + .../scripts/exampleScript.sh | 4 + .../tests/e2e/deployOnlyBase/main.test.bicep | 59 + .../e2e/deployOnlyImage/dependencies.bicep | 303 + .../tests/e2e/deployOnlyImage/main.test.bicep | 87 + .../deployOnlyImage/scripts/exampleScript.sh | 4 + .../azure-image-builder/version.json | 7 + .../scripts/Wait-ForImageBuild.ps1 | 97 + .../helper/Invoke-ResourceRemoval.ps1 | 58 + 34 files changed, 20894 insertions(+) create mode 100644 .github/workflows/avm.ptn.virtual-machine-images.azure-image-builder.yml create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/README.md create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/main.bicep create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/main.json create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/src/icons/AzureComputeGalleries.svg create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Deployment-Script.png create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/src/icons/ImageTemplates.svg create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Managed-identities.svg create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Network-Security-Groups.svg create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Resource-Groups.svg create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Storage-Accounts.svg create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/src/icons/VMImageDefinitions.svg create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/src/icons/VMImageVersions.svg create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Virtual-Machine-Scale-Sets.svg create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Virtual-Networks.svg create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/src/image/imageBuilderimage.png create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/defaults/main.test.bicep create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/main.test.bicep create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/scripts/Initialize-LinuxSoftware.ps1 create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/scripts/Initialize-WindowsSoftware.ps1 create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/scripts/Install-LinuxPowerShell.sh create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/scripts/Install-WindowsPowerShell.ps1 create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyAssetsAndImage/dependencies.bicep create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyAssetsAndImage/main.test.bicep create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyAssetsAndImage/scripts/exampleScript.sh create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyBase/main.test.bicep create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyImage/dependencies.bicep create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyImage/main.test.bicep create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyImage/scripts/exampleScript.sh create mode 100644 avm/ptn/virtual-machine-images/azure-image-builder/version.json create mode 100644 avm/utilities/e2e-template-assets/scripts/Wait-ForImageBuild.ps1 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2bdd9e91d3..f67f52ec14 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -23,6 +23,7 @@ /avm/ptn/network/private-link-private-dns-zones/ @Azure/avm-ptn-network-privatelinkprivatednszones-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/policy-insights/remediation/ @Azure/avm-ptn-policyinsights-remediation-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/ptn/security/security-center/ @Azure/avm-ptn-security-securitycenter-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/ptn/virtual-machine-images/azure-image-builder/ @Azure/avm-ptn-virtualmachineimages-azureimagebuilder-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/aad/domain-service/ @Azure/avm-res-aad-domainservice-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/alerts-management/action-rule/ @Azure/avm-res-alertsmanagement-actionrule-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/analysis-services/server/ @Azure/avm-res-analysisservices-server-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index fccb58bce5..d30ee744be 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -58,6 +58,7 @@ body: - "avm/ptn/network/private-link-private-dns-zones" - "avm/ptn/policy-insights/remediation" - "avm/ptn/security/security-center" + - "avm/ptn/virtual-machine-images/azure-image-builder" - "avm/res/aad/domain-service" - "avm/res/alerts-management/action-rule" - "avm/res/analysis-services/server" diff --git a/.github/workflows/avm.ptn.virtual-machine-images.azure-image-builder.yml b/.github/workflows/avm.ptn.virtual-machine-images.azure-image-builder.yml new file mode 100644 index 0000000000..445bdfa481 --- /dev/null +++ b/.github/workflows/avm.ptn.virtual-machine-images.azure-image-builder.yml @@ -0,0 +1,88 @@ +name: "avm.ptn.virtual-machine-images.azure-image-builder" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.ptn.virtual-machine-images.azure-image-builder.yml" + - "avm/ptn/virtual-machine-images/azure-image-builder/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/ptn/virtual-machine-images/azure-image-builder" + workflowPath: ".github/workflows/avm.ptn.virtual-machine-images.azure-image-builder.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath }}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath }}" + secrets: inherit diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/README.md b/avm/ptn/virtual-machine-images/azure-image-builder/README.md new file mode 100644 index 0000000000..0b1e08858b --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/README.md @@ -0,0 +1,1159 @@ +# Custom Images using Azure Image Builder `[VirtualMachineImages/AzureImageBuilder]` + +This module provides you with a packaged solution to create custom images using the Azure Image Builder service publishing to an Azure Compute Gallery. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Notes](#Notes) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Compute/galleries` | [2022-03-03](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2022-03-03/galleries) | +| `Microsoft.Compute/galleries/applications` | [2022-03-03](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2022-03-03/galleries/applications) | +| `Microsoft.Compute/galleries/images` | [2022-03-03](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Compute/2022-03-03/galleries/images) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.ManagedIdentity/userAssignedIdentities` | [2023-01-31](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ManagedIdentity/2023-01-31/userAssignedIdentities) | +| `Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials` | [2023-01-31](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ManagedIdentity/2023-01-31/userAssignedIdentities/federatedIdentityCredentials) | +| `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/virtualNetworks` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/virtualNetworks) | +| `Microsoft.Network/virtualNetworks/subnets` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/virtualNetworks/subnets) | +| `Microsoft.Network/virtualNetworks/virtualNetworkPeerings` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/virtualNetworks/virtualNetworkPeerings) | +| `Microsoft.Resources/deploymentScripts` | [2023-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Resources/2023-08-01/deploymentScripts) | +| `Microsoft.Resources/resourceGroups` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Resources/2024-03-01/resourceGroups) | +| `Microsoft.Storage/storageAccounts` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts) | +| `Microsoft.Storage/storageAccounts/blobServices` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices) | +| `Microsoft.Storage/storageAccounts/blobServices/containers` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers) | +| `Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers/immutabilityPolicies) | +| `Microsoft.Storage/storageAccounts/fileServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/fileServices) | +| `Microsoft.Storage/storageAccounts/fileServices/shares` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-01-01/storageAccounts/fileServices/shares) | +| `Microsoft.Storage/storageAccounts/localUsers` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/localUsers) | +| `Microsoft.Storage/storageAccounts/managementPolicies` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-01-01/storageAccounts/managementPolicies) | +| `Microsoft.Storage/storageAccounts/queueServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/queueServices) | +| `Microsoft.Storage/storageAccounts/queueServices/queues` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/queueServices/queues) | +| `Microsoft.Storage/storageAccounts/tableServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/tableServices) | +| `Microsoft.Storage/storageAccounts/tableServices/tables` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/tableServices/tables) | +| `Microsoft.VirtualMachineImages/imageTemplates` | [2023-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.VirtualMachineImages/imageTemplates) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/ptn/virtual-machine-images/azure-image-builder:`. + +- [Using small parameter set](#example-1-using-small-parameter-set) +- [Deploying all resources](#example-2-deploying-all-resources) +- [Deploying only the assets & image](#example-3-deploying-only-the-assets-image) +- [Deploying only the base services](#example-4-deploying-only-the-base-services) +- [Deploying only the image](#example-5-deploying-only-the-image) + +### Example 1: _Using small parameter set_ + +This instance deploys the module with min features enabled. + + +

+ +via Bicep module + +```bicep +module azureImageBuilder 'br/public:avm/ptn/virtual-machine-images/azure-image-builder:' = { + name: 'azureImageBuilderDeployment' + params: { + // Required parameters + computeGalleryImageDefinitionName: '' + computeGalleryImageDefinitions: [ + { + hyperVGeneration: 'V2' + name: 'sid-linux' + offer: 'devops_linux' + osType: 'Linux' + publisher: 'devops' + sku: 'devops_linux_az' + } + ] + computeGalleryName: 'galapvmiaibmin' + imageTemplateImageSource: { + offer: 'ubuntu-24_04-lts' + publisher: 'canonical' + sku: 'server' + type: 'PlatformImage' + version: 'latest' + } + // Non-required parameters + assetsStorageAccountName: 'stapvmiaibmin' + deploymentsToPerform: '' + location: '' + resourceGroupName: '' + } +} +``` + +
+

+ +

+ +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 + "computeGalleryImageDefinitionName": { + "value": "" + }, + "computeGalleryImageDefinitions": { + "value": [ + { + "hyperVGeneration": "V2", + "name": "sid-linux", + "offer": "devops_linux", + "osType": "Linux", + "publisher": "devops", + "sku": "devops_linux_az" + } + ] + }, + "computeGalleryName": { + "value": "galapvmiaibmin" + }, + "imageTemplateImageSource": { + "value": { + "offer": "ubuntu-24_04-lts", + "publisher": "canonical", + "sku": "server", + "type": "PlatformImage", + "version": "latest" + } + }, + // Non-required parameters + "assetsStorageAccountName": { + "value": "stapvmiaibmin" + }, + "deploymentsToPerform": { + "value": "" + }, + "location": { + "value": "" + }, + "resourceGroupName": { + "value": "" + } + } +} +``` + +
+

+ +### Example 2: _Deploying all resources_ + +This instance deploys the module with the conditions set up to deploy all resource and build the image. + + +

+ +via Bicep module + +```bicep +module azureImageBuilder 'br/public:avm/ptn/virtual-machine-images/azure-image-builder:' = { + name: 'azureImageBuilderDeployment' + params: { + // Required parameters + computeGalleryImageDefinitionName: '' + computeGalleryImageDefinitions: [ + { + hyperVGeneration: 'V2' + name: '' + offer: 'devops_linux' + osType: 'Linux' + publisher: 'devops' + sku: 'devops_linux_az' + } + ] + computeGalleryName: 'galapvmiaiba' + imageTemplateImageSource: { + offer: '0001-com-ubuntu-server-jammy' + publisher: 'canonical' + sku: '22_04-lts-gen2' + type: 'PlatformImage' + version: 'latest' + } + // Non-required parameters + assetsStorageAccountContainerName: '' + assetsStorageAccountName: '' + deploymentsToPerform: '' + imageTemplateCustomizationSteps: [ + { + name: 'PowerShell installation' + scriptUri: '' + type: 'Shell' + } + { + destination: '' + name: '' + sourceUri: '' + type: 'File' + } + { + inline: [ + 'pwsh \'\'' + ] + name: 'Software installation' + type: 'Shell' + } + ] + location: '' + resourceGroupName: '' + storageAccountFilesToUpload: [ + { + name: '' + value: '' + } + { + name: '' + value: '' + } + ] + } +} +``` + +
+

+ +

+ +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 + "computeGalleryImageDefinitionName": { + "value": "" + }, + "computeGalleryImageDefinitions": { + "value": [ + { + "hyperVGeneration": "V2", + "name": "", + "offer": "devops_linux", + "osType": "Linux", + "publisher": "devops", + "sku": "devops_linux_az" + } + ] + }, + "computeGalleryName": { + "value": "galapvmiaiba" + }, + "imageTemplateImageSource": { + "value": { + "offer": "0001-com-ubuntu-server-jammy", + "publisher": "canonical", + "sku": "22_04-lts-gen2", + "type": "PlatformImage", + "version": "latest" + } + }, + // Non-required parameters + "assetsStorageAccountContainerName": { + "value": "" + }, + "assetsStorageAccountName": { + "value": "" + }, + "deploymentsToPerform": { + "value": "" + }, + "imageTemplateCustomizationSteps": { + "value": [ + { + "name": "PowerShell installation", + "scriptUri": "", + "type": "Shell" + }, + { + "destination": "", + "name": "", + "sourceUri": "", + "type": "File" + }, + { + "inline": [ + "pwsh \"\"" + ], + "name": "Software installation", + "type": "Shell" + } + ] + }, + "location": { + "value": "" + }, + "resourceGroupName": { + "value": "" + }, + "storageAccountFilesToUpload": { + "value": [ + { + "name": "", + "value": "" + }, + { + "name": "", + "value": "" + } + ] + } + } +} +``` + +
+

+ +### Example 3: _Deploying only the assets & image_ + +This instance deploys the module with the conditions set up to only update the assets on the assets storage account and build the image, assuming all dependencies are setup. + + +

+ +via Bicep module + +```bicep +module azureImageBuilder 'br/public:avm/ptn/virtual-machine-images/azure-image-builder:' = { + name: 'azureImageBuilderDeployment' + params: { + // Required parameters + computeGalleryImageDefinitionName: '' + computeGalleryImageDefinitions: '' + computeGalleryName: '' + imageTemplateImageSource: { + offer: 'ubuntu-24_04-lts' + publisher: 'canonical' + sku: 'server' + type: 'PlatformImage' + version: 'latest' + } + // Non-required parameters + assetsStorageAccountContainerName: '' + assetsStorageAccountName: '' + deploymentScriptManagedIdentityName: '' + deploymentScriptStorageAccountName: '' + deploymentScriptSubnetName: '' + deploymentsToPerform: 'Only assets & image' + imageManagedIdentityName: '' + imageSubnetName: '' + imageTemplateCustomizationSteps: [ + { + name: 'Example script' + scriptUri: '' + type: 'Shell' + } + ] + imageTemplateResourceGroupName: '' + location: '' + resourceGroupName: '' + storageAccountFilesToUpload: [ + { + name: '' + value: '' + } + ] + virtualNetworkName: '' + } +} +``` + +
+

+ +

+ +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 + "computeGalleryImageDefinitionName": { + "value": "" + }, + "computeGalleryImageDefinitions": { + "value": "" + }, + "computeGalleryName": { + "value": "" + }, + "imageTemplateImageSource": { + "value": { + "offer": "ubuntu-24_04-lts", + "publisher": "canonical", + "sku": "server", + "type": "PlatformImage", + "version": "latest" + } + }, + // Non-required parameters + "assetsStorageAccountContainerName": { + "value": "" + }, + "assetsStorageAccountName": { + "value": "" + }, + "deploymentScriptManagedIdentityName": { + "value": "" + }, + "deploymentScriptStorageAccountName": { + "value": "" + }, + "deploymentScriptSubnetName": { + "value": "" + }, + "deploymentsToPerform": { + "value": "Only assets & image" + }, + "imageManagedIdentityName": { + "value": "" + }, + "imageSubnetName": { + "value": "" + }, + "imageTemplateCustomizationSteps": { + "value": [ + { + "name": "Example script", + "scriptUri": "", + "type": "Shell" + } + ] + }, + "imageTemplateResourceGroupName": { + "value": "" + }, + "location": { + "value": "" + }, + "resourceGroupName": { + "value": "" + }, + "storageAccountFilesToUpload": { + "value": [ + { + "name": "", + "value": "" + } + ] + }, + "virtualNetworkName": { + "value": "" + } + } +} +``` + +
+

+ +### Example 4: _Deploying only the base services_ + +This instance deploys the module with the conditions set up to only deploy the base resources, that is everything but the image. + + +

+ +via Bicep module + +```bicep +module azureImageBuilder 'br/public:avm/ptn/virtual-machine-images/azure-image-builder:' = { + name: 'azureImageBuilderDeployment' + params: { + // Required parameters + computeGalleryImageDefinitionName: '' + computeGalleryImageDefinitions: [ + { + hyperVGeneration: 'V2' + name: '' + offer: 'devops_linux' + osType: 'Linux' + publisher: 'devops' + sku: 'devops_linux_az' + } + ] + computeGalleryName: 'galapvmiaibob' + imageTemplateImageSource: { + offer: 'ubuntu-24_04-lts' + publisher: 'canonical' + sku: 'server' + type: 'PlatformImage' + version: 'latest' + } + // Non-required parameters + assetsStorageAccountName: 'stapvmiaibob' + deploymentsToPerform: 'Only base' + imageManagedIdentityName: 'msi-it-apvmiaibob' + location: '' + resourceGroupName: '' + } +} +``` + +
+

+ +

+ +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 + "computeGalleryImageDefinitionName": { + "value": "" + }, + "computeGalleryImageDefinitions": { + "value": [ + { + "hyperVGeneration": "V2", + "name": "", + "offer": "devops_linux", + "osType": "Linux", + "publisher": "devops", + "sku": "devops_linux_az" + } + ] + }, + "computeGalleryName": { + "value": "galapvmiaibob" + }, + "imageTemplateImageSource": { + "value": { + "offer": "ubuntu-24_04-lts", + "publisher": "canonical", + "sku": "server", + "type": "PlatformImage", + "version": "latest" + } + }, + // Non-required parameters + "assetsStorageAccountName": { + "value": "stapvmiaibob" + }, + "deploymentsToPerform": { + "value": "Only base" + }, + "imageManagedIdentityName": { + "value": "msi-it-apvmiaibob" + }, + "location": { + "value": "" + }, + "resourceGroupName": { + "value": "" + } + } +} +``` + +
+

+ +### Example 5: _Deploying only the image_ + +This instance deploys the module with the conditions set up to only deploy and bake the image, assuming all dependencies are setup. + + +

+ +via Bicep module + +```bicep +module azureImageBuilder 'br/public:avm/ptn/virtual-machine-images/azure-image-builder:' = { + name: 'azureImageBuilderDeployment' + params: { + // Required parameters + computeGalleryImageDefinitionName: '' + computeGalleryImageDefinitions: '' + computeGalleryName: '' + imageTemplateImageSource: { + offer: 'ubuntu-24_04-lts' + publisher: 'canonical' + sku: 'server' + type: 'PlatformImage' + version: 'latest' + } + // Non-required parameters + deploymentScriptManagedIdentityName: '' + deploymentScriptStorageAccountName: '' + deploymentScriptSubnetName: '' + deploymentsToPerform: 'Only image' + imageManagedIdentityName: '' + imageSubnetName: '' + imageTemplateCustomizationSteps: [ + { + name: 'Example script' + scriptUri: '' + type: 'Shell' + } + ] + imageTemplateResourceGroupName: '' + location: '' + resourceGroupName: '' + virtualNetworkName: '' + } +} +``` + +
+

+ +

+ +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 + "computeGalleryImageDefinitionName": { + "value": "" + }, + "computeGalleryImageDefinitions": { + "value": "" + }, + "computeGalleryName": { + "value": "" + }, + "imageTemplateImageSource": { + "value": { + "offer": "ubuntu-24_04-lts", + "publisher": "canonical", + "sku": "server", + "type": "PlatformImage", + "version": "latest" + } + }, + // Non-required parameters + "deploymentScriptManagedIdentityName": { + "value": "" + }, + "deploymentScriptStorageAccountName": { + "value": "" + }, + "deploymentScriptSubnetName": { + "value": "" + }, + "deploymentsToPerform": { + "value": "Only image" + }, + "imageManagedIdentityName": { + "value": "" + }, + "imageSubnetName": { + "value": "" + }, + "imageTemplateCustomizationSteps": { + "value": [ + { + "name": "Example script", + "scriptUri": "", + "type": "Shell" + } + ] + }, + "imageTemplateResourceGroupName": { + "value": "" + }, + "location": { + "value": "" + }, + "resourceGroupName": { + "value": "" + }, + "virtualNetworkName": { + "value": "" + } + } +} +``` + +
+

+ +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`computeGalleryImageDefinitionName`](#parameter-computegalleryimagedefinitionname) | string | The name of Image Definition of the Azure Compute Gallery to host the new image version. | +| [`computeGalleryImageDefinitions`](#parameter-computegalleryimagedefinitions) | array | The Image Definitions in the Azure Compute Gallery. | +| [`computeGalleryName`](#parameter-computegalleryname) | string | The name of the Azure Compute Gallery. | +| [`imageTemplateImageSource`](#parameter-imagetemplateimagesource) | object | The image source to use for the Image Template. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`assetsStorageAccountContainerName`](#parameter-assetsstorageaccountcontainername) | string | The name of container in the Storage Account. | +| [`assetsStorageAccountName`](#parameter-assetsstorageaccountname) | string | The name of the storage account. Only needed if you want to upload scripts to be used during image baking. | +| [`deploymentScriptManagedIdentityName`](#parameter-deploymentscriptmanagedidentityname) | string | The name of the Managed Identity used by deployment scripts. | +| [`deploymentScriptStorageAccountName`](#parameter-deploymentscriptstorageaccountname) | string | The name of the storage account. | +| [`deploymentScriptSubnetName`](#parameter-deploymentscriptsubnetname) | string | The name of the Image Template Virtual Network Subnet to create. | +| [`deploymentsToPerform`](#parameter-deploymentstoperform) | string | A parameter to control which deployments should be executed. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`imageManagedIdentityName`](#parameter-imagemanagedidentityname) | string | The name of the Managed Identity used by the Azure Image Builder. | +| [`imageSubnetName`](#parameter-imagesubnetname) | string | The name of the Image Template Virtual Network Subnet to create. | +| [`imageTemplateCustomizationSteps`](#parameter-imagetemplatecustomizationsteps) | array | The customization steps to use for the Image Template. | +| [`imageTemplateDeploymentScriptName`](#parameter-imagetemplatedeploymentscriptname) | string | The name of the Deployment Script to trigger the image template baking. | +| [`imageTemplateName`](#parameter-imagetemplatename) | string | The name of the Image Template. | +| [`imageTemplateResourceGroupName`](#parameter-imagetemplateresourcegroupname) | string | The name of the Resource Group to deploy the Image Template resources into. | +| [`location`](#parameter-location) | string | The location to deploy into. | +| [`resourceGroupName`](#parameter-resourcegroupname) | string | The name of the Resource Group. | +| [`storageAccountFilesToUpload`](#parameter-storageaccountfilestoupload) | array | The files to upload to the Assets Storage Account. | +| [`storageDeploymentScriptName`](#parameter-storagedeploymentscriptname) | string | The name of the Deployment Script to upload files to the assets storage account. | +| [`virtualNetworkAddressPrefix`](#parameter-virtualnetworkaddressprefix) | string | The address space of the Virtual Network. | +| [`virtualNetworkDeploymentScriptSubnetAddressPrefix`](#parameter-virtualnetworkdeploymentscriptsubnetaddressprefix) | string | The address space of the Virtual Network Subnet used by the deployment script. | +| [`virtualNetworkName`](#parameter-virtualnetworkname) | string | The name of the Virtual Network. | +| [`virtualNetworkSubnetAddressPrefix`](#parameter-virtualnetworksubnetaddressprefix) | string | The address space of the Virtual Network Subnet. | +| [`waitDeploymentScriptName`](#parameter-waitdeploymentscriptname) | string | The name of the Deployment Script to wait for for the image baking to conclude. | +| [`waitForImageBuild`](#parameter-waitforimagebuild) | bool | A parameter to control if the deployment should wait for the image build to complete. | +| [`waitForImageBuildTimeout`](#parameter-waitforimagebuildtimeout) | string | A parameter to control the timeout of the deployment script waiting for the image build. | + +**Generated parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`baseTime`](#parameter-basetime) | string | Do not provide a value! This date value is used to generate a SAS token to access the modules. | + +### Parameter: `computeGalleryImageDefinitionName` + +The name of Image Definition of the Azure Compute Gallery to host the new image version. + +- Required: Yes +- Type: string + +### Parameter: `computeGalleryImageDefinitions` + +The Image Definitions in the Azure Compute Gallery. + +- Required: Yes +- Type: array + +### Parameter: `computeGalleryName` + +The name of the Azure Compute Gallery. + +- Required: Yes +- Type: string + +### Parameter: `imageTemplateImageSource` + +The image source to use for the Image Template. + +- Required: Yes +- Type: object + +### Parameter: `assetsStorageAccountContainerName` + +The name of container in the Storage Account. + +- Required: No +- Type: string +- Default: `'aibscripts'` + +### Parameter: `assetsStorageAccountName` + +The name of the storage account. Only needed if you want to upload scripts to be used during image baking. + +- Required: No +- Type: string + +### Parameter: `deploymentScriptManagedIdentityName` + +The name of the Managed Identity used by deployment scripts. + +- Required: No +- Type: string +- Default: `'msi-ds'` + +### Parameter: `deploymentScriptStorageAccountName` + +The name of the storage account. + +- Required: No +- Type: string +- Default: `[format('{0}ds', parameters('assetsStorageAccountName'))]` + +### Parameter: `deploymentScriptSubnetName` + +The name of the Image Template Virtual Network Subnet to create. + +- Required: No +- Type: string +- Default: `'subnet-ds'` + +### Parameter: `deploymentsToPerform` + +A parameter to control which deployments should be executed. + +- Required: No +- Type: string +- Default: `'Only assets & image'` +- Allowed: + ```Bicep + [ + 'All' + 'Only assets & image' + 'Only base' + 'Only image' + ] + ``` + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `imageManagedIdentityName` + +The name of the Managed Identity used by the Azure Image Builder. + +- Required: No +- Type: string +- Default: `'msi-aib'` + +### Parameter: `imageSubnetName` + +The name of the Image Template Virtual Network Subnet to create. + +- Required: No +- Type: string +- Default: `'subnet-it'` + +### Parameter: `imageTemplateCustomizationSteps` + +The customization steps to use for the Image Template. + +- Required: No +- Type: array + +### Parameter: `imageTemplateDeploymentScriptName` + +The name of the Deployment Script to trigger the image template baking. + +- Required: No +- Type: string +- Default: `'ds-triggerBuild-imageTemplate'` + +### Parameter: `imageTemplateName` + +The name of the Image Template. + +- Required: No +- Type: string +- Default: `'it-aib'` + +### Parameter: `imageTemplateResourceGroupName` + +The name of the Resource Group to deploy the Image Template resources into. + +- Required: No +- Type: string +- Default: `[format('{0}-image-build', parameters('resourceGroupName'))]` + +### Parameter: `location` + +The location to deploy into. + +- Required: No +- Type: string +- Default: `[deployment().location]` + +### Parameter: `resourceGroupName` + +The name of the Resource Group. + +- Required: No +- Type: string +- Default: `'rg-ado-agents'` + +### Parameter: `storageAccountFilesToUpload` + +The files to upload to the Assets Storage Account. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-storageaccountfilestouploadname) | string | The name of the environment variable. | +| [`secureValue`](#parameter-storageaccountfilestouploadsecurevalue) | securestring | The value of the secure environment variable. | +| [`value`](#parameter-storageaccountfilestouploadvalue) | string | The value of the environment variable. | + +### Parameter: `storageAccountFilesToUpload.name` + +The name of the environment variable. + +- Required: Yes +- Type: string + +### Parameter: `storageAccountFilesToUpload.secureValue` + +The value of the secure environment variable. + +- Required: No +- Type: securestring + +### Parameter: `storageAccountFilesToUpload.value` + +The value of the environment variable. + +- Required: No +- Type: string + +### Parameter: `storageDeploymentScriptName` + +The name of the Deployment Script to upload files to the assets storage account. + +- Required: No +- Type: string +- Default: `'ds-triggerUpload-storage'` + +### Parameter: `virtualNetworkAddressPrefix` + +The address space of the Virtual Network. + +- Required: No +- Type: string +- Default: `'10.0.0.0/16'` + +### Parameter: `virtualNetworkDeploymentScriptSubnetAddressPrefix` + +The address space of the Virtual Network Subnet used by the deployment script. + +- Required: No +- Type: string +- Default: `[cidrSubnet(parameters('virtualNetworkAddressPrefix'), 24, 1)]` + +### Parameter: `virtualNetworkName` + +The name of the Virtual Network. + +- Required: No +- Type: string +- Default: `'vnet-it'` + +### Parameter: `virtualNetworkSubnetAddressPrefix` + +The address space of the Virtual Network Subnet. + +- Required: No +- Type: string +- Default: `[cidrSubnet(parameters('virtualNetworkAddressPrefix'), 24, 0)]` + +### Parameter: `waitDeploymentScriptName` + +The name of the Deployment Script to wait for for the image baking to conclude. + +- Required: No +- Type: string +- Default: `'ds-wait-imageTemplate-build'` + +### Parameter: `waitForImageBuild` + +A parameter to control if the deployment should wait for the image build to complete. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `waitForImageBuildTimeout` + +A parameter to control the timeout of the deployment script waiting for the image build. + +- Required: No +- Type: string +- Default: `'PT1H'` + +### Parameter: `baseTime` + +Do not provide a value! This date value is used to generate a SAS token to access the modules. + +- Required: No +- Type: string +- Default: `[utcNow()]` + +## Outputs + +_None_ + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `br/public:avm/res/compute/gallery:0.4.0` | Remote reference | +| `br/public:avm/res/managed-identity/user-assigned-identity:0.2.2` | Remote reference | +| `br/public:avm/res/network/virtual-network:0.1.6` | Remote reference | +| `br/public:avm/res/resources/deployment-script:0.3.1` | Remote reference | +| `br/public:avm/res/storage/storage-account:0.9.1` | Remote reference | +| `br/public:avm/res/virtual-machine-images/image-template:0.3.1` | Remote reference | + +## Notes + + +### Prerequisites + +The deployments described in the following sections assume certain prerequisites to be in place prior to deployment. + +- The deployment principal (e.g., the Service Principal tied to the deploying Service Connection) must have at least `Contributor` & `User Access Adminitrator` permissions on the target subscription to be able to deploy both resources and assign permissions to created user-assigned identities +- If you have a policy in place that prevents Storage Accounts from being deployed without a Firewall, you have to create an exemption for the Image Template / Staging Resource Group you can configure for the Image Template Resource (parameter `imageTemplateResourceGroupName`). The rationale is that the Azure-Image-Builder service uses this resource group to deploy both temporal resources used during the image build (e.g., a Virtual Machine), as well as a Storage Account to store temporal files & a 'packerlogs/customization.log' file in (which contains the logs of the image build). This Storage Account has no firewall configured, has a random name, and cannot be configured at deploy time. + +### Elements +The image creation uses several components: + +|     | Resource | Description | +|--|--|--| +| ResourceGroup | Resource Group | The resource group hosting the image resources | +| ResourceGroup | (Image) Resource Group | The resource group hosting the resources created during the image build | +| Storage Account | (Assets) Storage Account | The storage account that hosts the image customization scripts used by the _Azure Image Building_ when executing the image template. | +| Storage Account | (DS) Storage Account | The storage account that hosts the files of the Deployment Scripts. Required for private networking. | +| Managed Identity | (Image) User-Assigned Managed Identity | Azure Active Directory feature that eliminates the need for credentials in code, rotates credentials automatically, and reduces identity maintenance. In the context of the imaging construct, the managed identity (MSI) is used by the Image Builder Service. It is assigned contributor permissions on the subscription to be able to bake the image. Further, it is assigned read permissions on the Assets Storage Account Container in order to consume the customization scripts. | +| Managed Identity | (DS) User-Assigned Managed Identity | Azure Active Directory feature that eliminates the need for credentials in code, rotates credentials automatically, and reduces identity maintenance. In the context of the imaging construct, the managed identity (MSI) is used by the Image Builder Service. It's assigned permissions on the Image Template to trigger it, the Deployment Script Storage Account for Private Networking, and the Assets Storage Account to upload files. | +| Managed Identity | (Storage) Deployment Script | The Deployment Script that uploads the customization scripts to the Assets Storage Account. | +| Managed Identity | (Trigger) Deployment Script | The Deployment Script that triggers the Image Template build. | +| Azure Compute Gallery | Azure Compute Gallery | Azure service that helps to build structure and organization for managed images. Provides global replication, versioning, grouping, sharing across subscriptions and scaling. The plain resource in itself is like an empty container. | +| Azure Compute Gallery Image | Azure Compute Gallery Image | Created within a gallery and contains information about the image and requirements for using it internally. This includes metadata like whether the image is Windows or Linux, release notes and recommended compute resources. Like the image gallery itself it acts like a container for the actual images. | +| Image Template | Image Template | A standard Azure Image Builder template that defines the parameters for building a custom image with AIB. The parameters include image source (Marketplace, custom image, etc.), customization options (i.e., Updates, scripts, restarts), and distribution (i.e., managed image, Azure Compute Gallery). The template is not an actual resource. Instead, when an image template is created, Azure stores all the metadata of the referenced Azure Compute Gallery Image alongside other image backing instructions as a hidden resource in a temporary resource group. | +| Image Version | Image Version | An image version (for example `0.24322.55884`) is what you use to create a VM when using a gallery. You can have multiple versions of an image as needed for your environment. This value **cannot** be chosen. | + +

+ +Run workflow + +### First deployment +When triggering the deployment for the first time, make sure you either select `All` or `Only base` for the `deploymentsToPerform` parameter. In either case the template will deploy all resources and scripts you will subsequently need to create the images. For any subsequent run, you can go with any option you need. + +The steps the _Azure Image Builder_ performs on the image are defined by elements configured in the `customizationSteps` parameter of the image template parameter file. In our setup we [Usage Examples](#usage-examples) we use one or multiple custom scripts that are uploaded by the template to a storage account ahead of the image deployment. + +### Mermaid Graphs + +The following graphs show which services are created based on the chosen `deploymentsToPerform`. As such, they show a (simplified) view of the order and relations in between the included deployments. + +#### (Simplified) All +```mermaid + graph TD; + imageTemplateRg --> imageTemplate + rg --> vnet + rg --> dsMsi + rg --> imageMSI + rg --> azureComputeGallery + + azureComputeGallery --> imageTemplate + + imageMSI --> imageMSI_rbac + + dsMsi --> assetsStorageAccount + imageMSI --> assetsStorageAccount + + dsMsi --> dsStorageAccount + vnet --> dsStorageAccount + + dsStorageAccount --> storageAccount_upload + assetsStorageAccount --> storageAccount_upload + dsMsi --> imageTemplate + storageAccount_upload ==> imageTemplate + + imageTemplate --> imageTemplate_trigger + + imageTemplate_trigger ==> imageTemplate_wait + imageMSI_rbac ==> imageTemplate +``` + + + +#### (Simplified) Only base +```mermaid + graph TD; + rg -- provides value to --> azureComputeGallery + rg -- provides value to --> vnet + rg -- provides value to --> dsMsi + rg -- provides value to --> imageMSI + + imageTemplateRg + + vnet -- provides value to --> dsStorageAccount + + dsMsi -- provides value to --> dsStorageAccount + dsStorageAccount -- provides value to --> storageAccount_upload + assetsStorageAccount -- provides value to --> storageAccount_upload + + dsMsi -- provides value to --> assetsStorageAccount + imageMSI -- provides value to --> assetsStorageAccount + imageMSI -- provides value to --> imageMSI_rbac +``` + +#### Only assets & image +Assumes all other services + permissions are deployed +```mermaid + graph TD; + imageTemplateRg -- provides value to --> imageTemplate + storageAccount_upload -- must come after --> imageTemplate + imageTemplate -- provides value to --> imageTemplate_trigger + imageTemplate -- provides value to --> imageTemplate_wait + imageTemplate_trigger -- must come after --> imageTemplate_wait +``` + +#### Only image +Assumes all other services + permissions are deployed +```mermaid + graph TD; + imageTemplateRg -- provides value to --> imageTemplate + imageTemplate -- provides value to --> imageTemplate_trigger + imageTemplate -- provides value to --> imageTemplate_wait + imageTemplate_trigger -- must come after --> imageTemplate_wait +``` + +### Troubleshooting + +Most commonly issues with the construct occur during the image building process due to script errors. As those are hard to troubleshoot and the AIB VMs that are used to bake images are not accessible, the AIB service writes logs into a storage account in the 'staging' resource group it generates during the building process as documented [here](https://docs.microsoft.com/en-us/azure/virtual-machines/linux/image-builder-troubleshoot#customization-log). + +Aside from the packer logs, it will also contain the logs generated by the provided customization scripts and hence provide you insights into 'where' something wrong, and ideally also 'what' went wrong. + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/main.bicep b/avm/ptn/virtual-machine-images/azure-image-builder/main.bicep new file mode 100644 index 0000000000..41c05593b8 --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/main.bicep @@ -0,0 +1,625 @@ +targetScope = 'subscription' + +metadata name = 'Custom Images using Azure Image Builder' +metadata description = 'This module provides you with a packaged solution to create custom images using the Azure Image Builder service publishing to an Azure Compute Gallery.' +metadata owner = 'AlexanderSehr' + +// ================ // +// Input Parameters // +// ================ // + +@description('Optional. A parameter to control which deployments should be executed.') +@allowed([ + 'All' + 'Only base' + 'Only assets & image' + 'Only image' +]) +param deploymentsToPerform string = 'Only assets & image' + +// Resource Group Parameters +@description('Optional. The name of the Resource Group.') +param resourceGroupName string = 'rg-ado-agents' + +@description('Optional. The name of the Resource Group to deploy the Image Template resources into.') +param imageTemplateResourceGroupName string = '${resourceGroupName}-image-build' + +// User Assigned Identity (MSI) Parameters +@description('Optional. The name of the Managed Identity used by deployment scripts.') +param deploymentScriptManagedIdentityName string = 'msi-ds' + +@description('Optional. The name of the Managed Identity used by the Azure Image Builder.') +param imageManagedIdentityName string = 'msi-aib' + +// Azure Compute Gallery Parameters +@description('Required. The name of the Azure Compute Gallery.') +param computeGalleryName string + +@description('Required. The Image Definitions in the Azure Compute Gallery.') +param computeGalleryImageDefinitions array + +// Storage Account Parameters +@description('Optional. The name of the storage account. Only needed if you want to upload scripts to be used during image baking.') +param assetsStorageAccountName string? + +@description('Optional. The name of the storage account.') +param deploymentScriptStorageAccountName string = '${assetsStorageAccountName}ds' + +@description('Optional. The name of container in the Storage Account.') +param assetsStorageAccountContainerName string = 'aibscripts' + +// Virtual Network Parameters +@description('Optional. The name of the Virtual Network.') +param virtualNetworkName string = 'vnet-it' + +@description('Optional. The address space of the Virtual Network.') +param virtualNetworkAddressPrefix string = '10.0.0.0/16' + +@description('Optional. The name of the Image Template Virtual Network Subnet to create.') +param imageSubnetName string = 'subnet-it' + +@description('Optional. The address space of the Virtual Network Subnet.') +param virtualNetworkSubnetAddressPrefix string = cidrSubnet(virtualNetworkAddressPrefix, 24, 0) + +@description('Optional. The name of the Image Template Virtual Network Subnet to create.') +param deploymentScriptSubnetName string = 'subnet-ds' + +@description('Optional. The address space of the Virtual Network Subnet used by the deployment script.') +param virtualNetworkDeploymentScriptSubnetAddressPrefix string = cidrSubnet(virtualNetworkAddressPrefix, 24, 1) + +// Deployment Script Parameters +@description('Optional. The name of the Deployment Script to upload files to the assets storage account.') +param storageDeploymentScriptName string = 'ds-triggerUpload-storage' + +@description('Optional. The files to upload to the Assets Storage Account.') +param storageAccountFilesToUpload storageAccountFilesToUploadType[]? + +@description('Optional. The name of the Deployment Script to trigger the image template baking.') +param imageTemplateDeploymentScriptName string = 'ds-triggerBuild-imageTemplate' + +@description('Optional. The name of the Deployment Script to wait for for the image baking to conclude.') +param waitDeploymentScriptName string = 'ds-wait-imageTemplate-build' + +// Image Template Parameters +@description('Optional. The name of the Image Template.') +param imageTemplateName string = 'it-aib' + +@description('Required. The image source to use for the Image Template.') +param imageTemplateImageSource object + +@description('Optional. The customization steps to use for the Image Template.') +@minLength(1) +param imageTemplateCustomizationSteps array? + +@description('Required. The name of Image Definition of the Azure Compute Gallery to host the new image version.') +param computeGalleryImageDefinitionName string + +@description('Optional. A parameter to control if the deployment should wait for the image build to complete.') +param waitForImageBuild bool = true + +@description('Optional. A parameter to control the timeout of the deployment script waiting for the image build.') +param waitForImageBuildTimeout string = 'PT1H' + +// Shared Parameters +@description('Optional. The location to deploy into.') +param location string = deployment().location + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Generated. Do not provide a value! This date value is used to generate a SAS token to access the modules.') +param baseTime string = utcNow() + +var formattedTime = replace(replace(replace(baseTime, ':', ''), '-', ''), ' ', '') + +// Role required for deployment script to be able to use a storage account via private networking +resource storageFileDataPrivilegedContributorRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: '69566ab7-960f-475b-8e7c-b3118f30c6bd' // Storage File Data Priveleged Contributor + scope: tenant() +} +resource contributorRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: 'b24988ac-6180-42a0-ab88-20f7382dd24c' // Contributor + scope: tenant() +} + +// =========== // +// Deployments // +// =========== // + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) { + name: '46d3xbcp.ptn.vmimages-azureimagebuilder.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + location: location + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } +} + +////////////////////////// +// START: ALL // +// START: ONLY BASE // +// ==================== // + +// Resource Groups +resource rg 'Microsoft.Resources/resourceGroups@2024-03-01' = if (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only base') { + name: resourceGroupName + location: location +} + +// Always deployed as both an infra element & needed as a staging resource group for image building +resource imageTemplateRg 'Microsoft.Resources/resourceGroups@2024-03-01' = if (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only base') { + name: imageTemplateResourceGroupName + location: location +} + +// User Assigned Identity (MSI) +module dsMsi 'br/public:avm/res/managed-identity/user-assigned-identity:0.2.2' = if (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only base') { + name: '${deployment().name}-ds-msi' + scope: rg + params: { + name: deploymentScriptManagedIdentityName + location: location + enableTelemetry: enableTelemetry + } +} + +module imageMSI 'br/public:avm/res/managed-identity/user-assigned-identity:0.2.2' = if (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only base') { + name: '${deployment().name}-image-msi' + scope: rg + params: { + name: imageManagedIdentityName + location: location + enableTelemetry: enableTelemetry + } +} + +// MSI Subscription contributor assignment +resource imageMSI_rbac 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only base') { + // name: guid(subscription().subscriptionId, imageManagedIdentityName, contributorRole.id) + name: guid( + subscription().id, + '${subscription().id}/resourceGroups/${resourceGroupName}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/${imageManagedIdentityName}', + contributorRole.id + ) + properties: { + // TODO: Requries conditions. Tracked issue: https://github.com/Azure/bicep/issues/2371 + principalId: (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only base') + ? imageMSI.outputs.principalId + : '' + roleDefinitionId: contributorRole.id + principalType: 'ServicePrincipal' + } +} + +// Azure Compute Gallery +module azureComputeGallery 'br/public:avm/res/compute/gallery:0.4.0' = if (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only base') { + name: '${deployment().name}-acg' + scope: rg + params: { + name: computeGalleryName + images: computeGalleryImageDefinitions + location: location + enableTelemetry: enableTelemetry + } +} + +// Image Template Virtual Network +module vnet 'br/public:avm/res/network/virtual-network:0.1.6' = if (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only base') { + name: '${deployment().name}-vnet' + scope: rg + params: { + name: virtualNetworkName + addressPrefixes: [ + virtualNetworkAddressPrefix + ] + subnets: [ + { + name: imageSubnetName + addressPrefix: virtualNetworkSubnetAddressPrefix + privateLinkServiceNetworkPolicies: 'Disabled' // Required if using Azure Image Builder with existing VNET + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + } + ] + } + { + name: deploymentScriptSubnetName + addressPrefix: virtualNetworkDeploymentScriptSubnetAddressPrefix + privateLinkServiceNetworkPolicies: 'Disabled' // Required if using Azure Image Builder with existing VNET - temp + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + } + ] + delegations: [ + { + name: 'Microsoft.ContainerInstance.containerGroups' + properties: { + serviceName: 'Microsoft.ContainerInstance/containerGroups' + } + } + ] + } + ] + location: location + enableTelemetry: enableTelemetry + } +} + +// Assets Storage Account +module assetsStorageAccount 'br/public:avm/res/storage/storage-account:0.9.1' = if (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only base') { + name: '${deployment().name}-files-sa' + scope: rg + params: { + name: assetsStorageAccountName! + allowSharedKeyAccess: false // Keys not needed if MSI is granted access + enableTelemetry: enableTelemetry + location: location + networkAcls: { + // NOTE: If Firewall is enabled, it causes the Image Template to not be able to connect to the storage account. It's NOT a permission issue (ref: https://github.com/danielsollondon/azvmimagebuilder/issues/31#issuecomment-1793779854) + defaultAction: 'Allow' + // defaultAction: 'Deny' + // virtualNetworkRules: [ + // { + // // Allow image template to access data + // action: 'Allow' + // id: vnet.outputs.subnetResourceIds[0] // imageSubnet + // } + // { + // // Allow deployment script to access storage account to upload data + // action: 'Allow' + // id: vnet.outputs.subnetResourceIds[1] // deploymentScriptSubnet + // } + // ] + } + blobServices: { + containers: [ + { + name: assetsStorageAccountContainerName + publicAccess: 'None' + roleAssignments: [ + { + // Allow Infra MSI to access storage account container to upload files - DO NOT REMOVE + roleDefinitionIdOrName: 'Storage Blob Data Contributor' + principalId: (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only base') + ? dsMsi.outputs.principalId + : '' // Requires condition als Bicep will otherwise try to resolve the null reference + principalType: 'ServicePrincipal' + } + { + // Allow image MSI to access storage account container to read files - DO NOT REMOVE + roleDefinitionIdOrName: 'Storage Blob Data Reader' // 'Storage Blob Data Reader' + principalId: (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only base') + ? imageMSI.outputs.principalId + : '' // Requires condition als Bicep will otherwise try to resolve the null reference + principalType: 'ServicePrincipal' + } + ] + } + ] + containerDeleteRetentionPolicyEnabled: true + containerDeleteRetentionPolicyDays: 10 + } + } +} + +// Deployment scripts & their storage account +module dsStorageAccount 'br/public:avm/res/storage/storage-account:0.9.1' = if (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only base') { + name: '${deployment().name}-ds-sa' + scope: rg + params: { + name: deploymentScriptStorageAccountName + allowSharedKeyAccess: true // May not be disabled to allow deployment script to access storage account files + enableTelemetry: enableTelemetry + roleAssignments: [ + { + // Allow MSI to leverage the storage account for private networking of container instance + // ref: https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/deployment-script-bicep#access-private-virtual-network + roleDefinitionIdOrName: storageFileDataPrivilegedContributorRole.id // Storage File Data Priveleged Contributor + principalId: (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only base') + ? dsMsi.outputs.principalId + : '' // Requires condition als Bicep will otherwise try to resolve the null reference + principalType: 'ServicePrincipal' + } + ] + location: location + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + virtualNetworkRules: [ + { + // Allow deployment script to use storage account for private networking of container instance + action: 'Allow' + id: resourceId( + subscription().subscriptionId, + resourceGroupName, + 'Microsoft.Network/virtualNetworks/subnets', + virtualNetworkName, + deploymentScriptSubnetName + ) + } + ] + } + } + dependsOn: [ + vnet + ] +} + +//////////////////////////////////// +// START: ONLY ASSETS & IMAGE // +// ============================== // + +// Upload storage account files +module storageAccount_upload 'br/public:avm/res/resources/deployment-script:0.3.1' = if (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only base' || deploymentsToPerform == 'Only assets & image') { + name: '${deployment().name}-storage-upload-ds' + scope: resourceGroup(resourceGroupName) + params: { + name: '${storageDeploymentScriptName}-${formattedTime}' + kind: 'AzurePowerShell' + azPowerShellVersion: '12.0' + enableTelemetry: enableTelemetry + managedIdentities: { + userAssignedResourcesIds: [ + resourceId( + subscription().subscriptionId, + resourceGroupName, + 'Microsoft.ManagedIdentity/userAssignedIdentities', + deploymentScriptManagedIdentityName + ) + ] + } + scriptContent: loadTextContent('../../../utilities/e2e-template-assets/scripts/Set-StorageContainerContentByEnvVar.ps1') + // environmentVariables: [ + // map(range(0, length(storageAccountFilesToUpload ?? [])), index => { + // name: '__SCRIPT__${storageAccountFilesToUpload![index].name}' + // value: storageAccountFilesToUpload![index].?value + // secureValue: storageAccountFilesToUpload![index].?secureValue + // }) + // ] + environmentVariables: map(storageAccountFilesToUpload ?? [], file => { + name: '__SCRIPT__${replace(replace(file.name, '-', '__'), '.', '_') }' // May only be alphanumeric characters & underscores. The upload will replace '_' with '.' and '__' with '-'. E.g., Install__LinuxPowerShell_sh will be Install-LinuxPowerShell.sh + value: file.?value + secureValue: file.?secureValue + }) + arguments: ' -StorageAccountName "${assetsStorageAccountName}" -TargetContainer "${assetsStorageAccountContainerName}"' + timeout: 'PT30M' + cleanupPreference: 'Always' + location: location + storageAccountResourceId: resourceId( + subscription().subscriptionId, + resourceGroupName, + 'Microsoft.Storage/storageAccounts', + deploymentScriptStorageAccountName + ) + subnetResourceIds: [ + resourceId( + subscription().subscriptionId, + resourceGroupName, + 'Microsoft.Network/virtualNetworks/subnets', + virtualNetworkName, + deploymentScriptSubnetName + ) + ] + } + dependsOn: [ + // Conditionally required + rg + assetsStorageAccount + dsMsi + dsStorageAccount + vnet + ] +} + +// ================== // +// END: ONLY BASE // +//////////////////////// + +/////////////////////////// +// START: ONLY IMAGE // +// ===================== // + +// Image template +resource dsMsi_existing 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (deploymentsToPerform == 'Only assets & image' || deploymentsToPerform == 'Only image') { + name: deploymentScriptManagedIdentityName + scope: resourceGroup(resourceGroupName) +} + +module imageTemplate 'br/public:avm/res/virtual-machine-images/image-template:0.3.1' = if (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only assets & image' || deploymentsToPerform == 'Only image') { + name: '${deployment().name}-it' + scope: resourceGroup(resourceGroupName) + params: { + customizationSteps: imageTemplateCustomizationSteps + imageSource: imageTemplateImageSource + name: imageTemplateName + enableTelemetry: enableTelemetry + managedIdentities: { + userAssignedResourceIds: [ + resourceId( + subscription().subscriptionId, + resourceGroupName, + 'Microsoft.ManagedIdentity/userAssignedIdentities', + imageManagedIdentityName + ) + ] + } + distributions: [ + { + type: 'SharedImage' + sharedImageGalleryImageDefinitionResourceId: resourceId( + subscription().subscriptionId, + resourceGroupName, + 'Microsoft.Compute/galleries/images', + computeGalleryName, + computeGalleryImageDefinitionName + ) + } + ] + + // subnetResourceId: vnet.outputs.subnetResourceIds[0] // Image Subnet + subnetResourceId: resourceId( + subscription().subscriptionId, + resourceGroupName, + 'Microsoft.Network/virtualNetworks/subnets', + virtualNetworkName, + imageSubnetName + ) + location: location + stagingResourceGroupResourceId: imageTemplateRg.id + roleAssignments: [ + { + roleDefinitionIdOrName: 'Contributor' + // Allow deployment script to trigger image build. Use 'existing' reference if only part of solution is deployed + principalId: (deploymentsToPerform == 'Only assets & image' || deploymentsToPerform == 'Only image') + ? dsMsi_existing.properties.principalId + : dsMsi.outputs.principalId + principalType: 'ServicePrincipal' + } + ] + } + dependsOn: [ + storageAccount_upload + imageMSI_rbac + rg + imageMSI + azureComputeGallery + vnet + ] +} + +// Deployment script to trigger image build +module imageTemplate_trigger 'br/public:avm/res/resources/deployment-script:0.3.1' = if (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only assets & image' || deploymentsToPerform == 'Only image') { + name: '${deployment().name}-imageTemplate-trigger-ds' + scope: resourceGroup(resourceGroupName) + params: { + name: '${imageTemplateDeploymentScriptName}-${formattedTime}-${(deploymentsToPerform == 'All' || deploymentsToPerform == 'Only assets & image' || deploymentsToPerform == 'Only image') ? imageTemplate.outputs.name : ''}' // Requires condition als Bicep will otherwise try to resolve the null reference + kind: 'AzurePowerShell' + azPowerShellVersion: '12.0' + managedIdentities: { + userAssignedResourcesIds: [ + resourceId( + subscription().subscriptionId, + resourceGroupName, + 'Microsoft.ManagedIdentity/userAssignedIdentities', + deploymentScriptManagedIdentityName + ) + ] + } + enableTelemetry: enableTelemetry + scriptContent: (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only assets & image' || deploymentsToPerform == 'Only image') + ? imageTemplate.outputs.runThisCommand + : '' // Requires condition als Bicep will otherwise try to resolve the null reference + timeout: 'PT30M' + cleanupPreference: 'Always' + location: location + // storageAccountResourceId: dsStorageAccount.outputs.resourceId + storageAccountResourceId: resourceId( + subscription().subscriptionId, + resourceGroupName, + 'Microsoft.Storage/storageAccounts', + deploymentScriptStorageAccountName + ) + subnetResourceIds: [ + // vnet.outputs.subnetResourceIds[1] // deploymentScriptSubnet + resourceId( + subscription().subscriptionId, + resourceGroupName, + 'Microsoft.Network/virtualNetworks/subnets', + virtualNetworkName, + deploymentScriptSubnetName + ) + ] + } + dependsOn: [ + // Always required + imageTemplate + // Conditionally required + rg + dsMsi + dsStorageAccount + storageAccount_upload + vnet + ] +} + +module imageTemplate_wait 'br/public:avm/res/resources/deployment-script:0.3.1' = if (waitForImageBuild && (deploymentsToPerform == 'All' || deploymentsToPerform == 'Only assets & image' || deploymentsToPerform == 'Only image')) { + name: '${deployment().name}-imageTemplate-wait-ds' + scope: resourceGroup(resourceGroupName) + params: { + name: '${waitDeploymentScriptName}-${formattedTime}' + kind: 'AzurePowerShell' + azPowerShellVersion: '12.0' + managedIdentities: { + userAssignedResourcesIds: [ + resourceId( + subscription().subscriptionId, + resourceGroupName, + 'Microsoft.ManagedIdentity/userAssignedIdentities', + deploymentScriptManagedIdentityName + ) + ] + } + scriptContent: loadTextContent('../../../utilities/e2e-template-assets/scripts/Wait-ForImageBuild.ps1') + arguments: ' -ImageTemplateName "${imageTemplate.outputs.name}" -ResourceGroupName "${resourceGroupName}"' + timeout: waitForImageBuildTimeout + cleanupPreference: 'Always' + location: location + storageAccountResourceId: resourceId( + subscription().subscriptionId, + resourceGroupName, + 'Microsoft.Storage/storageAccounts', + deploymentScriptStorageAccountName + ) + subnetResourceIds: [ + resourceId( + subscription().subscriptionId, + resourceGroupName, + 'Microsoft.Network/virtualNetworks/subnets', + virtualNetworkName, + deploymentScriptSubnetName + ) + ] + } + dependsOn: [ + imageTemplate_trigger + rg + vnet + dsStorageAccount + dsMsi + ] +} + +// ============================= // +// END: ALL // +// END: ONLY ASSETS & IMAGE // +// END: ONLY IMAGE // +/////////////////////////////////// + +// =============== // +// Definitions // +// =============== // + +type storageAccountFilesToUploadType = { + @description('Required. The name of the environment variable.') + name: string + + @description('Required. The value of the secure environment variable.') + @secure() + secureValue: string? + + @description('Required. The value of the environment variable.') + value: string? +} diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/main.json b/avm/ptn/virtual-machine-images/azure-image-builder/main.json new file mode 100644 index 0000000000..408049bed0 --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/main.json @@ -0,0 +1,15487 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "12788075391199236027" + }, + "name": "Custom Images using Azure Image Builder", + "description": "This module provides you with a packaged solution to create custom images using the Azure Image Builder service publishing to an Azure Compute Gallery.", + "owner": "AlexanderSehr" + }, + "definitions": { + "storageAccountFilesToUploadType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the environment variable." + } + }, + "secureValue": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Required. The value of the secure environment variable." + } + }, + "value": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. The value of the environment variable." + } + } + } + } + }, + "parameters": { + "deploymentsToPerform": { + "type": "string", + "defaultValue": "Only assets & image", + "allowedValues": [ + "All", + "Only base", + "Only assets & image", + "Only image" + ], + "metadata": { + "description": "Optional. A parameter to control which deployments should be executed." + } + }, + "resourceGroupName": { + "type": "string", + "defaultValue": "rg-ado-agents", + "metadata": { + "description": "Optional. The name of the Resource Group." + } + }, + "imageTemplateResourceGroupName": { + "type": "string", + "defaultValue": "[format('{0}-image-build', parameters('resourceGroupName'))]", + "metadata": { + "description": "Optional. The name of the Resource Group to deploy the Image Template resources into." + } + }, + "deploymentScriptManagedIdentityName": { + "type": "string", + "defaultValue": "msi-ds", + "metadata": { + "description": "Optional. The name of the Managed Identity used by deployment scripts." + } + }, + "imageManagedIdentityName": { + "type": "string", + "defaultValue": "msi-aib", + "metadata": { + "description": "Optional. The name of the Managed Identity used by the Azure Image Builder." + } + }, + "computeGalleryName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Azure Compute Gallery." + } + }, + "computeGalleryImageDefinitions": { + "type": "array", + "metadata": { + "description": "Required. The Image Definitions in the Azure Compute Gallery." + } + }, + "assetsStorageAccountName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the storage account. Only needed if you want to upload scripts to be used during image baking." + } + }, + "deploymentScriptStorageAccountName": { + "type": "string", + "defaultValue": "[format('{0}ds', parameters('assetsStorageAccountName'))]", + "metadata": { + "description": "Optional. The name of the storage account." + } + }, + "assetsStorageAccountContainerName": { + "type": "string", + "defaultValue": "aibscripts", + "metadata": { + "description": "Optional. The name of container in the Storage Account." + } + }, + "virtualNetworkName": { + "type": "string", + "defaultValue": "vnet-it", + "metadata": { + "description": "Optional. The name of the Virtual Network." + } + }, + "virtualNetworkAddressPrefix": { + "type": "string", + "defaultValue": "10.0.0.0/16", + "metadata": { + "description": "Optional. The address space of the Virtual Network." + } + }, + "imageSubnetName": { + "type": "string", + "defaultValue": "subnet-it", + "metadata": { + "description": "Optional. The name of the Image Template Virtual Network Subnet to create." + } + }, + "virtualNetworkSubnetAddressPrefix": { + "type": "string", + "defaultValue": "[cidrSubnet(parameters('virtualNetworkAddressPrefix'), 24, 0)]", + "metadata": { + "description": "Optional. The address space of the Virtual Network Subnet." + } + }, + "deploymentScriptSubnetName": { + "type": "string", + "defaultValue": "subnet-ds", + "metadata": { + "description": "Optional. The name of the Image Template Virtual Network Subnet to create." + } + }, + "virtualNetworkDeploymentScriptSubnetAddressPrefix": { + "type": "string", + "defaultValue": "[cidrSubnet(parameters('virtualNetworkAddressPrefix'), 24, 1)]", + "metadata": { + "description": "Optional. The address space of the Virtual Network Subnet used by the deployment script." + } + }, + "storageDeploymentScriptName": { + "type": "string", + "defaultValue": "ds-triggerUpload-storage", + "metadata": { + "description": "Optional. The name of the Deployment Script to upload files to the assets storage account." + } + }, + "storageAccountFilesToUpload": { + "type": "array", + "items": { + "$ref": "#/definitions/storageAccountFilesToUploadType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The files to upload to the Assets Storage Account." + } + }, + "imageTemplateDeploymentScriptName": { + "type": "string", + "defaultValue": "ds-triggerBuild-imageTemplate", + "metadata": { + "description": "Optional. The name of the Deployment Script to trigger the image template baking." + } + }, + "waitDeploymentScriptName": { + "type": "string", + "defaultValue": "ds-wait-imageTemplate-build", + "metadata": { + "description": "Optional. The name of the Deployment Script to wait for for the image baking to conclude." + } + }, + "imageTemplateName": { + "type": "string", + "defaultValue": "it-aib", + "metadata": { + "description": "Optional. The name of the Image Template." + } + }, + "imageTemplateImageSource": { + "type": "object", + "metadata": { + "description": "Required. The image source to use for the Image Template." + } + }, + "imageTemplateCustomizationSteps": { + "type": "array", + "nullable": true, + "minLength": 1, + "metadata": { + "description": "Optional. The customization steps to use for the Image Template." + } + }, + "computeGalleryImageDefinitionName": { + "type": "string", + "metadata": { + "description": "Required. The name of Image Definition of the Azure Compute Gallery to host the new image version." + } + }, + "waitForImageBuild": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. A parameter to control if the deployment should wait for the image build to complete." + } + }, + "waitForImageBuildTimeout": { + "type": "string", + "defaultValue": "PT1H", + "metadata": { + "description": "Optional. A parameter to control the timeout of the deployment script waiting for the image build." + } + }, + "location": { + "type": "string", + "defaultValue": "[deployment().location]", + "metadata": { + "description": "Optional. The location to deploy into." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "baseTime": { + "type": "string", + "defaultValue": "[utcNow()]", + "metadata": { + "description": "Generated. Do not provide a value! This date value is used to generate a SAS token to access the modules." + } + } + }, + "variables": { + "$fxv#0": "<#\n.SYNOPSIS\nRun the Post-Deployment for the storage account deployment & upload required data to the storage account.\n\n.DESCRIPTION\nRun the Post-Deployment for the storage account deployment & upload required data to the storage account.\nAny content that should be uploaded must exist as an environment variable with a 'script_' prefix (for example 'script_Initialize-LinuxSoftware_ps1').\nThe script will fetch any matching environment variable, store it as a file (for example 'script_Initialize__LinuxSoftware_ps1' is stored as 'Initialize-LinuxSoftware.ps1')\nand uploade it as blob to the given container.\n\n.PARAMETER StorageAccountName\nRequired. The name of the Storage Account to upload to\n\n.PARAMETER TargetContainer\nRequired. The container to upload the files to\n\n.EXAMPLE\n. 'Set-StorageContainerContentByEnvVar.ps1' -StorageAccountName 'mystorage' -TargetContainer 'myContainer'\n\nUpload any required data to the storage account 'mystorage' and container 'myContainer'.\n#>\n\n[CmdletBinding(SupportsShouldProcess = $True)]\nparam(\n [Parameter(Mandatory = $true)]\n [string] $StorageAccountName,\n\n [Parameter(Mandatory = $true)]\n [string] $TargetContainer\n)\n\nWrite-Verbose 'Fetching & storing scripts' -Verbose\n$contentDirectoryName = 'scripts'\n$contentDirectory = (New-Item $contentDirectoryName -ItemType 'Directory' -Force).FullName\n$scriptPaths = @()\nforeach ($scriptEnvVar in (Get-ChildItem 'env:*').Name | Where-Object { $_ -like '__SCRIPT__*' }) {\n # Handle value like 'script_Initialize__LinuxSoftware_ps1'\n $scriptName = $scriptEnvVar -replace '__SCRIPT__', '' -replace '__', '-' -replace '_', '.'\n $scriptContent = (Get-Item env:$scriptEnvVar).Value\n\n Write-Verbose ('Storing file [{0}] with length [{1}]' -f $scriptName, $scriptContent.Length) -Verbose\n $scriptPaths += (New-Item (Join-Path $contentDirectoryName $scriptName) -ItemType 'File' -Value $scriptContent -Force).FullName\n}\n\nWrite-Verbose 'Getting storage account context.' -Verbose\n$ctx = New-AzStorageContext -StorageAccountName $storageAccountName -UseConnectedAccount\n\nWrite-Verbose 'Building paths to the local folders to upload.' -Verbose\nWrite-Verbose \"Content directory: '$contentDirectory'\" -Verbose\n\nforeach ($scriptPath in $scriptPaths) {\n\n try {\n Write-Verbose 'Testing blob container' -Verbose\n Get-AzStorageContainer -Name $targetContainer -Context $ctx -ErrorAction 'Stop'\n Write-Verbose 'Testing blob container SUCCEEDED' -Verbose\n\n Write-Verbose ('Uploading file [{0}] to container [{1}]' -f (Split-Path $scriptPath -Leaf), $TargetContainer) -Verbose\n if ($PSCmdlet.ShouldProcess(('File [{0}] to container [{1}]' -f (Split-Path $scriptPath -Leaf), $TargetContainer), 'Upload')) {\n $null = Set-AzStorageBlobContent -File $scriptPath -Container $targetContainer -Context $ctx -Force -ErrorAction 'Stop'\n }\n Write-Verbose 'Upload successful' -Verbose\n } catch {\n throw \"Upload FAILED: $_\"\n }\n}\n", + "$fxv#1": "<#\n.SYNOPSIS\nFetch the latest build status for the provided image template\n\n.DESCRIPTION\nFetch the latest build status for the provided image template\n\n.PARAMETER ResourceGroupName\nRequired. The name of the Resource Group containing the image template\n\n.PARAMETER ImageTemplateName\nRequired. The name of the image template to query to build status for. E.g. 'lin_it-2022-02-20-16-17-38'\n\n.EXAMPLE\n. 'Wait-ForImageBuild.ps1' -ResourceGroupName' 'myRG' -ImageTemplateName 'lin_it-2022-02-20-16-17-38'\n\nCheck the current build status of Image Template 'lin_it-2022-02-20-16-17-38' in Resource Group 'myRG'\n#>\n[CmdletBinding()]\nparam(\n [Parameter(Mandatory)]\n [string] $ResourceGroupName,\n\n [Parameter(Mandatory)]\n [string] $ImageTemplateName\n)\n\nbegin {\n Write-Debug ('[{0} entered]' -f $MyInvocation.MyCommand)\n}\n\nprocess {\n # Logic\n # -----\n $context = Get-AzContext\n $subscriptionId = $context.Subscription.Id\n $currentRetry = 1\n $maximumRetries = 720\n $timeToWait = 15\n $maxTimeCalc = '{0:hh\\:mm\\:ss}' -f [timespan]::fromseconds($maximumRetries * $timeToWait)\n do {\n\n # Runnning fetch in retry as it happened that the status was not available\n $statusFetchRetryCount = 3\n $statusFetchCurrentRetry = 1\n do {\n $path = '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.VirtualMachineImages/imageTemplates/{2}?api-version=2020-02-14' -f $subscriptionId, $ResourceGroupName, $ImageTemplateName\n $requestInputObject = @{\n Method = 'GET'\n Path = $path\n }\n\n $response = ((Invoke-AzRestMethod @requestInputObject).Content | ConvertFrom-Json).properties\n\n if ($response.lastRunStatus) {\n $latestStatus = $response.lastRunStatus\n break\n }\n Start-Sleep 5\n $statusFetchCurrentRetry++\n } while ($statusFetchCurrentRetry -le $statusFetchRetryCount)\n\n if (-not $latestStatus) {\n Write-Verbose ('Image Build failed with error: [{0}]' -f $response.provisioningError.message) -Verbose\n $latestStatus = 'failed'\n }\n\n\n if ($latestStatus -eq 'failed' -or $latestStatus.runState.ToLower() -eq 'failed') {\n $failedMessage = 'Image Template [{0}] build failed with status [{1}]. API reply: [{2}]' -f $ImageTemplateName, $latestStatus.runState, $response.lastRunStatus.message\n Write-Verbose $failedMessage -Verbose\n throw $failedMessage\n }\n\n if ($latestStatus.runState.ToLower() -notIn @('running', 'new')) {\n break\n }\n\n $currTimeCalc = '{0:hh\\:mm\\:ss}' -f [timespan]::fromseconds($currentRetry * $timeToWait)\n\n Write-Verbose ('[{0}] Waiting 15 seconds [{1}|{2}]' -f (Get-Date -Format 'HH:mm:ss'), $currTimeCalc, $maxTimeCalc) -Verbose\n $currentRetry++\n Start-Sleep $timeToWait\n } while ($currentRetry -le $maximumRetries)\n\n if ($latestStatus) {\n $duration = New-TimeSpan -Start $latestStatus.startTime -End $latestStatus.endTime\n Write-Verbose ('It took [{0}] minutes and [{1}] seconds to build and distribute the image.' -f $duration.Minutes, $duration.Seconds) -Verbose\n } else {\n Write-Warning \"Timeout at [$currTimeCalc]. Note, the Azure Image Builder may still succeed.\"\n }\n return $latestStatus\n}\n\nend {\n Write-Debug ('[{0} existed]' -f $MyInvocation.MyCommand)\n}\n", + "formattedTime": "[replace(replace(replace(parameters('baseTime'), ':', ''), '-', ''), ' ', '')]" + }, + "resources": { + "storageFileDataPrivilegedContributorRole": { + "existing": true, + "type": "Microsoft.Authorization/roleDefinitions", + "apiVersion": "2022-04-01", + "scope": "/", + "name": "69566ab7-960f-475b-8e7c-b3118f30c6bd" + }, + "contributorRole": { + "existing": true, + "type": "Microsoft.Authorization/roleDefinitions", + "apiVersion": "2022-04-01", + "scope": "/", + "name": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.ptn.vmimages-azureimagebuilder.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "location": "[parameters('location')]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "rg": { + "condition": "[or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only base'))]", + "type": "Microsoft.Resources/resourceGroups", + "apiVersion": "2024-03-01", + "name": "[parameters('resourceGroupName')]", + "location": "[parameters('location')]" + }, + "imageTemplateRg": { + "condition": "[or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only base'))]", + "type": "Microsoft.Resources/resourceGroups", + "apiVersion": "2024-03-01", + "name": "[parameters('imageTemplateResourceGroupName')]", + "location": "[parameters('location')]" + }, + "imageMSI_rbac": { + "condition": "[or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only base'))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(subscription().id, format('{0}/resourceGroups/{1}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{2}', subscription().id, parameters('resourceGroupName'), parameters('imageManagedIdentityName')), tenantResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c'))]", + "properties": { + "principalId": "[if(or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only base')), reference('imageMSI').outputs.principalId.value, '')]", + "roleDefinitionId": "[tenantResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "contributorRole", + "imageMSI" + ] + }, + "dsMsi_existing": { + "condition": "[or(equals(parameters('deploymentsToPerform'), 'Only assets & image'), equals(parameters('deploymentsToPerform'), 'Only image'))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "resourceGroup": "[parameters('resourceGroupName')]", + "name": "[parameters('deploymentScriptManagedIdentityName')]" + }, + "dsMsi": { + "condition": "[or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only base'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-ds-msi', deployment().name)]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('deploymentScriptManagedIdentityName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "8316967256052237432" + }, + "name": "User Assigned Identities", + "description": "This module deploys a User Assigned Identity.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "federatedIdentityCredentialsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the federated identity credential." + } + }, + "audiences": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of audiences that can appear in the issued token." + } + }, + "issuer": { + "type": "string", + "metadata": { + "description": "Required. The URL of the issuer to be trusted." + } + }, + "subject": { + "type": "string", + "metadata": { + "description": "Required. The identifier of the external identity." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the User Assigned Identity." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "federatedIdentityCredentials": { + "$ref": "#/definitions/federatedIdentityCredentialsType", + "metadata": { + "description": "Optional. The federated identity credentials list to indicate which token from the external IdP should be trusted by your application. Federated identity credentials are supported on applications only. A maximum of 20 federated identity credentials can be added per application object." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Managed Identity Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59')]", + "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.managedidentity-userassignedidentity.{0}.{1}', replace('0.2.2', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "userAssignedIdentity": { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "userAssignedIdentity_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.ManagedIdentity/userAssignedIdentities/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "userAssignedIdentity" + ] + }, + "userAssignedIdentity_roleAssignments": { + "copy": { + "name": "userAssignedIdentity_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ManagedIdentity/userAssignedIdentities/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "userAssignedIdentity" + ] + }, + "userAssignedIdentity_federatedIdentityCredentials": { + "copy": { + "name": "userAssignedIdentity_federatedIdentityCredentials", + "count": "[length(coalesce(parameters('federatedIdentityCredentials'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-UserMSI-FederatedIdentityCredential-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].name]" + }, + "userAssignedIdentityName": { + "value": "[parameters('name')]" + }, + "audiences": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].audiences]" + }, + "issuer": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].issuer]" + }, + "subject": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].subject]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "663270811232806628" + }, + "name": "User Assigned Identity Federated Identity Credential", + "description": "This module deploys a User Assigned Identity Federated Identity Credential.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "userAssignedIdentityName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent user assigned identity. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." + } + }, + "audiences": { + "type": "array", + "metadata": { + "description": "Required. The list of audiences that can appear in the issued token. Should be set to api://AzureADTokenExchange for Azure AD. It says what Microsoft identity platform should accept in the aud claim in the incoming token. This value represents Azure AD in your external identity provider and has no fixed value across identity providers - you might need to create a new application registration in your IdP to serve as the audience of this token." + } + }, + "issuer": { + "type": "string", + "metadata": { + "description": "Required. The URL of the issuer to be trusted. Must match the issuer claim of the external token being exchanged." + } + }, + "subject": { + "type": "string", + "metadata": { + "description": "Required. The identifier of the external software workload within the external identity provider. Like the audience value, it has no fixed format, as each IdP uses their own - sometimes a GUID, sometimes a colon delimited identifier, sometimes arbitrary strings. The value here must match the sub claim within the token presented to Azure AD." + } + } + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials", + "apiVersion": "2023-01-31", + "name": "[format('{0}/{1}', parameters('userAssignedIdentityName'), parameters('name'))]", + "properties": { + "audiences": "[parameters('audiences')]", + "issuer": "[parameters('issuer')]", + "subject": "[parameters('subject')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the federated identity credential." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the federated identity credential." + }, + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials', parameters('userAssignedIdentityName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the federated identity credential was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "userAssignedIdentity" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the user assigned identity." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the user assigned identity." + }, + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name'))]" + }, + "principalId": { + "type": "string", + "metadata": { + "description": "The principal ID (object ID) of the user assigned identity." + }, + "value": "[reference('userAssignedIdentity').principalId]" + }, + "clientId": { + "type": "string", + "metadata": { + "description": "The client ID (application ID) of the user assigned identity." + }, + "value": "[reference('userAssignedIdentity').clientId]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the user assigned identity was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('userAssignedIdentity', '2023-01-31', 'full').location]" + } + } + } + }, + "dependsOn": [ + "rg" + ] + }, + "imageMSI": { + "condition": "[or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only base'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-image-msi', deployment().name)]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('imageManagedIdentityName')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "8316967256052237432" + }, + "name": "User Assigned Identities", + "description": "This module deploys a User Assigned Identity.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "federatedIdentityCredentialsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the federated identity credential." + } + }, + "audiences": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of audiences that can appear in the issued token." + } + }, + "issuer": { + "type": "string", + "metadata": { + "description": "Required. The URL of the issuer to be trusted." + } + }, + "subject": { + "type": "string", + "metadata": { + "description": "Required. The identifier of the external identity." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the User Assigned Identity." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "federatedIdentityCredentials": { + "$ref": "#/definitions/federatedIdentityCredentialsType", + "metadata": { + "description": "Optional. The federated identity credentials list to indicate which token from the external IdP should be trusted by your application. Federated identity credentials are supported on applications only. A maximum of 20 federated identity credentials can be added per application object." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Managed Identity Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'e40ec5ca-96e0-45a2-b4ff-59039f2c2b59')]", + "Managed Identity Operator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f1a07417-d97a-45cb-824c-7a7467783830')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.managedidentity-userassignedidentity.{0}.{1}', replace('0.2.2', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "userAssignedIdentity": { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + }, + "userAssignedIdentity_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.ManagedIdentity/userAssignedIdentities/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "userAssignedIdentity" + ] + }, + "userAssignedIdentity_roleAssignments": { + "copy": { + "name": "userAssignedIdentity_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ManagedIdentity/userAssignedIdentities/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "userAssignedIdentity" + ] + }, + "userAssignedIdentity_federatedIdentityCredentials": { + "copy": { + "name": "userAssignedIdentity_federatedIdentityCredentials", + "count": "[length(coalesce(parameters('federatedIdentityCredentials'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-UserMSI-FederatedIdentityCredential-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].name]" + }, + "userAssignedIdentityName": { + "value": "[parameters('name')]" + }, + "audiences": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].audiences]" + }, + "issuer": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].issuer]" + }, + "subject": { + "value": "[coalesce(parameters('federatedIdentityCredentials'), createArray())[copyIndex()].subject]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "663270811232806628" + }, + "name": "User Assigned Identity Federated Identity Credential", + "description": "This module deploys a User Assigned Identity Federated Identity Credential.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "userAssignedIdentityName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent user assigned identity. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret." + } + }, + "audiences": { + "type": "array", + "metadata": { + "description": "Required. The list of audiences that can appear in the issued token. Should be set to api://AzureADTokenExchange for Azure AD. It says what Microsoft identity platform should accept in the aud claim in the incoming token. This value represents Azure AD in your external identity provider and has no fixed value across identity providers - you might need to create a new application registration in your IdP to serve as the audience of this token." + } + }, + "issuer": { + "type": "string", + "metadata": { + "description": "Required. The URL of the issuer to be trusted. Must match the issuer claim of the external token being exchanged." + } + }, + "subject": { + "type": "string", + "metadata": { + "description": "Required. The identifier of the external software workload within the external identity provider. Like the audience value, it has no fixed format, as each IdP uses their own - sometimes a GUID, sometimes a colon delimited identifier, sometimes arbitrary strings. The value here must match the sub claim within the token presented to Azure AD." + } + } + }, + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials", + "apiVersion": "2023-01-31", + "name": "[format('{0}/{1}', parameters('userAssignedIdentityName'), parameters('name'))]", + "properties": { + "audiences": "[parameters('audiences')]", + "issuer": "[parameters('issuer')]", + "subject": "[parameters('subject')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the federated identity credential." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the federated identity credential." + }, + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials', parameters('userAssignedIdentityName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the federated identity credential was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "userAssignedIdentity" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the user assigned identity." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the user assigned identity." + }, + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name'))]" + }, + "principalId": { + "type": "string", + "metadata": { + "description": "The principal ID (object ID) of the user assigned identity." + }, + "value": "[reference('userAssignedIdentity').principalId]" + }, + "clientId": { + "type": "string", + "metadata": { + "description": "The client ID (application ID) of the user assigned identity." + }, + "value": "[reference('userAssignedIdentity').clientId]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the user assigned identity was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('userAssignedIdentity', '2023-01-31', 'full').location]" + } + } + } + }, + "dependsOn": [ + "rg" + ] + }, + "azureComputeGallery": { + "condition": "[or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only base'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-acg', deployment().name)]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('computeGalleryName')]" + }, + "images": { + "value": "[parameters('computeGalleryImageDefinitions')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "9752194366887174506" + }, + "name": "Azure Compute Galleries", + "description": "This module deploys an Azure Compute Gallery (formerly known as Shared Image Gallery).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Required. Name of the Azure Compute Gallery." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description of the Azure Shared Image Gallery." + } + }, + "applications": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Applications to create." + } + }, + "images": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Images to create." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags for all resources." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "sharingProfile": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Profile for gallery sharing to subscription or tenant." + } + }, + "softDeletePolicy": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Soft deletion policy of the gallery." + } + } + }, + "variables": { + "builtInRoleNames": { + "Compute Gallery Sharing Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1ef6a3be-d0ac-425d-8c01-acb62866290b')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.compute-gallery.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "gallery": { + "type": "Microsoft.Compute/galleries", + "apiVersion": "2022-03-03", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "description": "[parameters('description')]", + "sharingProfile": "[parameters('sharingProfile')]", + "softDeletePolicy": "[parameters('softDeletePolicy')]" + } + }, + "gallery_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Compute/galleries/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "gallery" + ] + }, + "gallery_roleAssignments": { + "copy": { + "name": "gallery_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Compute/galleries/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Compute/galleries', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "gallery" + ] + }, + "galleries_applications": { + "copy": { + "name": "galleries_applications", + "count": "[length(coalesce(parameters('applications'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Gallery-Application-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "name": { + "value": "[coalesce(parameters('applications'), createArray())[copyIndex()].name]" + }, + "galleryName": { + "value": "[parameters('name')]" + }, + "supportedOSType": { + "value": "[coalesce(parameters('applications'), createArray())[copyIndex()].supportedOSType]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('applications'), createArray())[copyIndex()], 'description')]" + }, + "eula": { + "value": "[tryGet(coalesce(parameters('applications'), createArray())[copyIndex()], 'eula')]" + }, + "privacyStatementUri": { + "value": "[tryGet(coalesce(parameters('applications'), createArray())[copyIndex()], 'privacyStatementUri')]" + }, + "releaseNoteUri": { + "value": "[tryGet(coalesce(parameters('applications'), createArray())[copyIndex()], 'releaseNoteUri')]" + }, + "endOfLifeDate": { + "value": "[tryGet(coalesce(parameters('applications'), createArray())[copyIndex()], 'endOfLifeDate')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('applications'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "customActions": { + "value": "[tryGet(coalesce(parameters('applications'), createArray())[copyIndex()], 'customActions')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('applications'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "9175012718933553578" + }, + "name": "Compute Galleries Applications", + "description": "This module deploys an Azure Compute Gallery Application.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the application definition." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "galleryName": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Conditional. The name of the parent Azure Compute Gallery. Required if the template is used in a standalone deployment." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of this gallery Application Definition resource. This property is updatable." + } + }, + "eula": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Eula agreement for the gallery Application Definition. Has to be a valid URL." + } + }, + "privacyStatementUri": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The privacy statement uri. Has to be a valid URL." + } + }, + "releaseNoteUri": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The release note uri. Has to be a valid URL." + } + }, + "supportedOSType": { + "type": "string", + "allowedValues": [ + "Windows", + "Linux" + ], + "metadata": { + "description": "Required. This property allows you to specify the supported type of the OS that application is built for." + } + }, + "endOfLifeDate": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The end of life date of the gallery Image Definition. This property can be used for decommissioning purposes. This property is updatable. Allowed format: 2020-01-10T23:00:00.000Z." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags for all resources." + } + }, + "customActions": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. A list of custom actions that can be performed with all of the Gallery Application Versions within this Gallery Application." + } + } + }, + "variables": { + "builtInRoleNames": { + "Compute Gallery Sharing Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1ef6a3be-d0ac-425d-8c01-acb62866290b')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "gallery": { + "existing": true, + "type": "Microsoft.Compute/galleries", + "apiVersion": "2022-03-03", + "name": "[parameters('galleryName')]" + }, + "application": { + "type": "Microsoft.Compute/galleries/applications", + "apiVersion": "2022-03-03", + "name": "[format('{0}/{1}', parameters('galleryName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "customActions": "[parameters('customActions')]", + "description": "[parameters('description')]", + "endOfLifeDate": "[parameters('endOfLifeDate')]", + "eula": "[parameters('eula')]", + "privacyStatementUri": "[parameters('privacyStatementUri')]", + "releaseNoteUri": "[parameters('releaseNoteUri')]", + "supportedOSType": "[parameters('supportedOSType')]" + }, + "dependsOn": [ + "gallery" + ] + }, + "application_roleAssignments": { + "copy": { + "name": "application_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Compute/galleries/{0}/applications/{1}', parameters('galleryName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Compute/galleries/applications', parameters('galleryName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "application" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the image was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the image." + }, + "value": "[resourceId('Microsoft.Compute/galleries/applications', parameters('galleryName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the image." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('application', '2022-03-03', 'full').location]" + } + } + } + }, + "dependsOn": [ + "gallery" + ] + }, + "galleries_images": { + "copy": { + "name": "galleries_images", + "count": "[length(coalesce(parameters('images'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Gallery-Image-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "name": { + "value": "[coalesce(parameters('images'), createArray())[copyIndex()].name]" + }, + "galleryName": { + "value": "[parameters('name')]" + }, + "osType": { + "value": "[coalesce(parameters('images'), createArray())[copyIndex()].osType]" + }, + "osState": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'osState')]" + }, + "publisher": { + "value": "[coalesce(parameters('images'), createArray())[copyIndex()].publisher]" + }, + "offer": { + "value": "[coalesce(parameters('images'), createArray())[copyIndex()].offer]" + }, + "sku": { + "value": "[coalesce(parameters('images'), createArray())[copyIndex()].sku]" + }, + "minRecommendedvCPUs": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'minRecommendedvCPUs')]" + }, + "maxRecommendedvCPUs": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'maxRecommendedvCPUs')]" + }, + "minRecommendedMemory": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'minRecommendedMemory')]" + }, + "maxRecommendedMemory": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'maxRecommendedMemory')]" + }, + "hyperVGeneration": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'hyperVGeneration')]" + }, + "securityType": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'securityType')]" + }, + "isAcceleratedNetworkSupported": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'isAcceleratedNetworkSupported')]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'description')]" + }, + "eula": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'eula')]" + }, + "privacyStatementUri": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'privacyStatementUri')]" + }, + "releaseNoteUri": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'releaseNoteUri')]" + }, + "productName": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'productName')]" + }, + "planName": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'planName')]" + }, + "planPublisherName": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'planPublisherName')]" + }, + "endOfLife": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'endOfLife')]" + }, + "excludedDiskTypes": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'excludedDiskTypes')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('images'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "7059991596058545894" + }, + "name": "Compute Galleries Image Definitions", + "description": "This module deploys an Azure Compute Gallery Image Definition.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the image definition." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "galleryName": { + "type": "string", + "minLength": 1, + "metadata": { + "description": "Conditional. The name of the parent Azure Shared Image Gallery. Required if the template is used in a standalone deployment." + } + }, + "osType": { + "type": "string", + "allowedValues": [ + "Windows", + "Linux" + ], + "metadata": { + "description": "Required. OS type of the image to be created." + } + }, + "osState": { + "type": "string", + "defaultValue": "Generalized", + "allowedValues": [ + "Generalized", + "Specialized" + ], + "metadata": { + "description": "Optional. This property allows the user to specify whether the virtual machines created under this image are 'Generalized' or 'Specialized'." + } + }, + "publisher": { + "type": "string", + "metadata": { + "description": "Required. The name of the gallery Image Definition publisher." + } + }, + "offer": { + "type": "string", + "metadata": { + "description": "Required. The name of the gallery Image Definition offer." + } + }, + "sku": { + "type": "string", + "metadata": { + "description": "Required. The name of the gallery Image Definition SKU." + } + }, + "minRecommendedvCPUs": { + "type": "int", + "defaultValue": 1, + "minValue": 1, + "maxValue": 128, + "metadata": { + "description": "Optional. The minimum number of the CPU cores recommended for this image." + } + }, + "maxRecommendedvCPUs": { + "type": "int", + "defaultValue": 4, + "minValue": 1, + "maxValue": 128, + "metadata": { + "description": "Optional. The maximum number of the CPU cores recommended for this image." + } + }, + "minRecommendedMemory": { + "type": "int", + "defaultValue": 4, + "minValue": 1, + "maxValue": 4000, + "metadata": { + "description": "Optional. The minimum amount of RAM in GB recommended for this image." + } + }, + "maxRecommendedMemory": { + "type": "int", + "defaultValue": 16, + "minValue": 1, + "maxValue": 4000, + "metadata": { + "description": "Optional. The maximum amount of RAM in GB recommended for this image." + } + }, + "hyperVGeneration": { + "type": "string", + "nullable": true, + "allowedValues": [ + "V1", + "V2" + ], + "metadata": { + "description": "Optional. The hypervisor generation of the Virtual Machine.\n- If this value is not specified, then it is determined by the securityType parameter.\n- If the securityType parameter is specified, then the value of hyperVGeneration will be V2, else V1.\n" + } + }, + "securityType": { + "type": "string", + "defaultValue": "Standard", + "allowedValues": [ + "Standard", + "TrustedLaunch", + "ConfidentialVM", + "ConfidentialVMSupported" + ], + "metadata": { + "description": "Optional. The security type of the image. Requires a hyperVGeneration V2." + } + }, + "isHibernateSupported": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Specifiy if the image supports hibernation." + } + }, + "isAcceleratedNetworkSupported": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Specify if the image supports accelerated networking.\nAccelerated networking enables single root I/O virtualization (SR-IOV) to a VM, greatly improving its networking performance.\nThis high-performance path bypasses the host from the data path, which reduces latency, jitter, and CPU utilization for the most demanding network workloads on supported VM types.\n" + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of this gallery Image Definition resource. This property is updatable." + } + }, + "eula": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Eula agreement for the gallery Image Definition. Has to be a valid URL." + } + }, + "privacyStatementUri": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The privacy statement uri. Has to be a valid URL." + } + }, + "releaseNoteUri": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The release note uri. Has to be a valid URL." + } + }, + "productName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The product ID." + } + }, + "planName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The plan ID." + } + }, + "planPublisherName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The publisher ID." + } + }, + "endOfLife": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The end of life date of the gallery Image Definition. This property can be used for decommissioning purposes. This property is updatable. Allowed format: 2020-01-10T23:00:00.000Z." + } + }, + "excludedDiskTypes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of the excluded disk types (e.g., Standard_LRS)." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags for all resources." + } + } + }, + "variables": { + "builtInRoleNames": { + "Compute Gallery Sharing Admin": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '1ef6a3be-d0ac-425d-8c01-acb62866290b')]", + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "gallery": { + "existing": true, + "type": "Microsoft.Compute/galleries", + "apiVersion": "2022-03-03", + "name": "[parameters('galleryName')]" + }, + "image": { + "type": "Microsoft.Compute/galleries/images", + "apiVersion": "2022-03-03", + "name": "[format('{0}/{1}', parameters('galleryName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "osType": "[parameters('osType')]", + "osState": "[parameters('osState')]", + "identifier": { + "publisher": "[parameters('publisher')]", + "offer": "[parameters('offer')]", + "sku": "[parameters('sku')]" + }, + "recommended": { + "vCPUs": { + "min": "[parameters('minRecommendedvCPUs')]", + "max": "[parameters('maxRecommendedvCPUs')]" + }, + "memory": { + "min": "[parameters('minRecommendedMemory')]", + "max": "[parameters('maxRecommendedMemory')]" + } + }, + "hyperVGeneration": "[coalesce(parameters('hyperVGeneration'), if(not(empty(parameters('securityType'))), 'V2', 'V1'))]", + "features": "[union(createArray(createObject('name', 'IsAcceleratedNetworkSupported', 'value', format('{0}', parameters('isAcceleratedNetworkSupported'))), createObject('name', 'IsHibernateSupported', 'value', format('{0}', parameters('isHibernateSupported')))), if(not(equals(parameters('securityType'), 'Standard')), createArray(createObject('name', 'SecurityType', 'value', parameters('securityType'))), createArray()))]", + "description": "[parameters('description')]", + "eula": "[parameters('eula')]", + "privacyStatementUri": "[parameters('privacyStatementUri')]", + "releaseNoteUri": "[parameters('releaseNoteUri')]", + "purchasePlan": { + "product": "[parameters('productName')]", + "name": "[parameters('planName')]", + "publisher": "[parameters('planPublisherName')]" + }, + "endOfLifeDate": "[parameters('endOfLife')]", + "disallowed": { + "diskTypes": "[parameters('excludedDiskTypes')]" + } + }, + "dependsOn": [ + "gallery" + ] + }, + "image_roleAssignments": { + "copy": { + "name": "image_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Compute/galleries/{0}/images/{1}', parameters('galleryName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Compute/galleries/images', parameters('galleryName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "image" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the image was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the image." + }, + "value": "[resourceId('Microsoft.Compute/galleries/images', parameters('galleryName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the image." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('image', '2022-03-03', 'full').location]" + } + } + } + }, + "dependsOn": [ + "gallery" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed image gallery." + }, + "value": "[resourceId('Microsoft.Compute/galleries', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed image gallery." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed image gallery." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('gallery', '2022-03-03', 'full').location]" + }, + "imageResourceIds": { + "type": "array", + "metadata": { + "description": "The resource ids of the deployed images." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('images'), createArray()))))]", + "input": "[reference(format('galleries_images[{0}]', range(0, length(coalesce(parameters('images'), createArray())))[copyIndex()])).outputs.resourceId.value]" + } + } + } + } + }, + "dependsOn": [ + "rg" + ] + }, + "vnet": { + "condition": "[or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only base'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-vnet', deployment().name)]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('virtualNetworkName')]" + }, + "addressPrefixes": { + "value": [ + "[parameters('virtualNetworkAddressPrefix')]" + ] + }, + "subnets": { + "value": [ + { + "name": "[parameters('imageSubnetName')]", + "addressPrefix": "[parameters('virtualNetworkSubnetAddressPrefix')]", + "privateLinkServiceNetworkPolicies": "Disabled", + "serviceEndpoints": [ + { + "service": "Microsoft.Storage" + } + ] + }, + { + "name": "[parameters('deploymentScriptSubnetName')]", + "addressPrefix": "[parameters('virtualNetworkDeploymentScriptSubnetAddressPrefix')]", + "privateLinkServiceNetworkPolicies": "Disabled", + "serviceEndpoints": [ + { + "service": "Microsoft.Storage" + } + ], + "delegations": [ + { + "name": "Microsoft.ContainerInstance.containerGroups", + "properties": { + "serviceName": "Microsoft.ContainerInstance/containerGroups" + } + } + ] + } + ] + }, + "location": { + "value": "[parameters('location')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "18408205474040416108" + }, + "name": "Virtual Networks", + "description": "This module deploys a Virtual Network (vNet).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Virtual Network (vNet)." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "addressPrefixes": { + "type": "array", + "metadata": { + "description": "Required. An Array of 1 or more IP Address Prefixes for the Virtual Network." + } + }, + "subnets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An Array of subnets to deploy to the Virtual Network." + } + }, + "dnsServers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. DNS Servers associated to the Virtual Network." + } + }, + "ddosProtectionPlanResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "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." + } + }, + "peerings": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Virtual Network Peerings configurations." + } + }, + "vnetEncryption": { + "type": "bool", + "defaultValue": false, + "metadata": { + "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." + } + }, + "vnetEncryptionEnforcement": { + "type": "string", + "defaultValue": "AllowUnencrypted", + "allowedValues": [ + "AllowUnencrypted", + "DropUnencrypted" + ], + "metadata": { + "description": "Optional. If the encrypted VNet allows VM that does not support encryption. Can only be used when vnetEncryption is enabled." + } + }, + "flowTimeoutInMinutes": { + "type": "int", + "defaultValue": 0, + "maxValue": 30, + "metadata": { + "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." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-virtualnetwork.{0}.{1}', replace('0.1.6', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "virtualNetwork": { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "subnets", + "count": "[length(parameters('subnets'))]", + "input": { + "name": "[parameters('subnets')[copyIndex('subnets')].name]", + "properties": { + "addressPrefix": "[parameters('subnets')[copyIndex('subnets')].addressPrefix]", + "addressPrefixes": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'addressPrefixes'), parameters('subnets')[copyIndex('subnets')].addressPrefixes, createArray())]", + "applicationGatewayIPConfigurations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'applicationGatewayIPConfigurations'), parameters('subnets')[copyIndex('subnets')].applicationGatewayIPConfigurations, createArray())]", + "delegations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'delegations'), parameters('subnets')[copyIndex('subnets')].delegations, createArray())]", + "ipAllocations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'ipAllocations'), parameters('subnets')[copyIndex('subnets')].ipAllocations, createArray())]", + "natGateway": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'natGatewayResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].natGatewayResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].natGatewayResourceId), null())]", + "networkSecurityGroup": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'networkSecurityGroupResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].networkSecurityGroupResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].networkSecurityGroupResourceId), null())]", + "privateEndpointNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'privateEndpointNetworkPolicies'), parameters('subnets')[copyIndex('subnets')].privateEndpointNetworkPolicies, null())]", + "privateLinkServiceNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'privateLinkServiceNetworkPolicies'), parameters('subnets')[copyIndex('subnets')].privateLinkServiceNetworkPolicies, null())]", + "routeTable": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'routeTableResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].routeTableResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].routeTableResourceId), null())]", + "serviceEndpoints": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'serviceEndpoints'), parameters('subnets')[copyIndex('subnets')].serviceEndpoints, createArray())]", + "serviceEndpointPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'serviceEndpointPolicies'), parameters('subnets')[copyIndex('subnets')].serviceEndpointPolicies, createArray())]" + } + } + } + ], + "addressSpace": { + "addressPrefixes": "[parameters('addressPrefixes')]" + }, + "ddosProtectionPlan": "[if(not(empty(parameters('ddosProtectionPlanResourceId'))), createObject('id', parameters('ddosProtectionPlanResourceId')), null())]", + "dhcpOptions": "[if(not(empty(parameters('dnsServers'))), createObject('dnsServers', array(parameters('dnsServers'))), null())]", + "enableDdosProtection": "[not(empty(parameters('ddosProtectionPlanResourceId')))]", + "encryption": "[if(equals(parameters('vnetEncryption'), true()), createObject('enabled', parameters('vnetEncryption'), 'enforcement', parameters('vnetEncryptionEnforcement')), null())]", + "flowTimeoutInMinutes": "[if(not(equals(parameters('flowTimeoutInMinutes'), 0)), parameters('flowTimeoutInMinutes'), null())]" + } + }, + "virtualNetwork_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_diagnosticSettings": { + "copy": { + "name": "virtualNetwork_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_roleAssignments": { + "copy": { + "name": "virtualNetwork_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_subnets": { + "copy": { + "name": "virtualNetwork_subnets", + "count": "[length(parameters('subnets'))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-subnet-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualNetworkName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('subnets')[copyIndex()].name]" + }, + "addressPrefix": { + "value": "[parameters('subnets')[copyIndex()].addressPrefix]" + }, + "addressPrefixes": "[if(contains(parameters('subnets')[copyIndex()], 'addressPrefixes'), createObject('value', parameters('subnets')[copyIndex()].addressPrefixes), createObject('value', createArray()))]", + "applicationGatewayIPConfigurations": "[if(contains(parameters('subnets')[copyIndex()], 'applicationGatewayIPConfigurations'), createObject('value', parameters('subnets')[copyIndex()].applicationGatewayIPConfigurations), createObject('value', createArray()))]", + "delegations": "[if(contains(parameters('subnets')[copyIndex()], 'delegations'), createObject('value', parameters('subnets')[copyIndex()].delegations), createObject('value', createArray()))]", + "ipAllocations": "[if(contains(parameters('subnets')[copyIndex()], 'ipAllocations'), createObject('value', parameters('subnets')[copyIndex()].ipAllocations), createObject('value', createArray()))]", + "natGatewayResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'natGatewayResourceId'), createObject('value', parameters('subnets')[copyIndex()].natGatewayResourceId), createObject('value', ''))]", + "networkSecurityGroupResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'networkSecurityGroupResourceId'), createObject('value', parameters('subnets')[copyIndex()].networkSecurityGroupResourceId), createObject('value', ''))]", + "privateEndpointNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'privateEndpointNetworkPolicies'), createObject('value', parameters('subnets')[copyIndex()].privateEndpointNetworkPolicies), createObject('value', ''))]", + "privateLinkServiceNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'privateLinkServiceNetworkPolicies'), createObject('value', parameters('subnets')[copyIndex()].privateLinkServiceNetworkPolicies), createObject('value', ''))]", + "roleAssignments": "[if(contains(parameters('subnets')[copyIndex()], 'roleAssignments'), createObject('value', parameters('subnets')[copyIndex()].roleAssignments), createObject('value', createArray()))]", + "routeTableResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'routeTableResourceId'), createObject('value', parameters('subnets')[copyIndex()].routeTableResourceId), createObject('value', ''))]", + "serviceEndpointPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'serviceEndpointPolicies'), createObject('value', parameters('subnets')[copyIndex()].serviceEndpointPolicies), createObject('value', createArray()))]", + "serviceEndpoints": "[if(contains(parameters('subnets')[copyIndex()], 'serviceEndpoints'), createObject('value', parameters('subnets')[copyIndex()].serviceEndpoints), createObject('value', createArray()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "17306638026226376877" + }, + "name": "Virtual Network Subnets", + "description": "This module deploys a Virtual Network Subnet.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Optional. The Name of the subnet resource." + } + }, + "virtualNetworkName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual network. Required if the template is used in a standalone deployment." + } + }, + "addressPrefix": { + "type": "string", + "metadata": { + "description": "Required. The address prefix for the subnet." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the network security group to assign to the subnet." + } + }, + "routeTableResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpoints": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "delegations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. The delegations to enable on the subnet." + } + }, + "natGatewayResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "Disabled", + "Enabled", + "" + ], + "metadata": { + "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "Disabled", + "Enabled", + "" + ], + "metadata": { + "description": "Optional. enable or disable apply network policies on private link service in the subnet." + } + }, + "addressPrefixes": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of address prefixes for the subnet." + } + }, + "applicationGatewayIPConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application gateway IP configurations of virtual network resource." + } + }, + "ipAllocations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of IpAllocation which reference this subnet." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "virtualNetwork": { + "existing": true, + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2023-04-01", + "name": "[parameters('virtualNetworkName')]" + }, + "subnet": { + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "properties": { + "addressPrefix": "[parameters('addressPrefix')]", + "networkSecurityGroup": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('id', parameters('networkSecurityGroupResourceId')), null())]", + "routeTable": "[if(not(empty(parameters('routeTableResourceId'))), createObject('id', parameters('routeTableResourceId')), null())]", + "natGateway": "[if(not(empty(parameters('natGatewayResourceId'))), createObject('id', parameters('natGatewayResourceId')), null())]", + "serviceEndpoints": "[parameters('serviceEndpoints')]", + "delegations": "[parameters('delegations')]", + "privateEndpointNetworkPolicies": "[if(not(empty(parameters('privateEndpointNetworkPolicies'))), parameters('privateEndpointNetworkPolicies'), null())]", + "privateLinkServiceNetworkPolicies": "[if(not(empty(parameters('privateLinkServiceNetworkPolicies'))), parameters('privateLinkServiceNetworkPolicies'), null())]", + "addressPrefixes": "[parameters('addressPrefixes')]", + "applicationGatewayIPConfigurations": "[parameters('applicationGatewayIPConfigurations')]", + "ipAllocations": "[parameters('ipAllocations')]", + "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "subnet_roleAssignments": { + "copy": { + "name": "subnet_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}/subnets/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "subnet" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name'))]" + }, + "subnetAddressPrefix": { + "type": "string", + "metadata": { + "description": "The address prefix for the subnet." + }, + "value": "[reference('subnet').addressPrefix]" + }, + "subnetAddressPrefixes": { + "type": "array", + "metadata": { + "description": "List of address prefixes for the subnet." + }, + "value": "[if(not(empty(parameters('addressPrefixes'))), reference('subnet').addressPrefixes, createArray())]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_peering_local": { + "copy": { + "name": "virtualNetwork_peering_local", + "count": "[length(parameters('peerings'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-virtualNetworkPeering-local-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "localVnetName": { + "value": "[parameters('name')]" + }, + "remoteVirtualNetworkId": { + "value": "[parameters('peerings')[copyIndex()].remoteVirtualNetworkId]" + }, + "name": "[if(contains(parameters('peerings')[copyIndex()], 'name'), createObject('value', parameters('peerings')[copyIndex()].name), createObject('value', format('{0}-{1}', parameters('name'), last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')))))]", + "allowForwardedTraffic": "[if(contains(parameters('peerings')[copyIndex()], 'allowForwardedTraffic'), createObject('value', parameters('peerings')[copyIndex()].allowForwardedTraffic), createObject('value', true()))]", + "allowGatewayTransit": "[if(contains(parameters('peerings')[copyIndex()], 'allowGatewayTransit'), createObject('value', parameters('peerings')[copyIndex()].allowGatewayTransit), createObject('value', false()))]", + "allowVirtualNetworkAccess": "[if(contains(parameters('peerings')[copyIndex()], 'allowVirtualNetworkAccess'), createObject('value', parameters('peerings')[copyIndex()].allowVirtualNetworkAccess), createObject('value', true()))]", + "doNotVerifyRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'doNotVerifyRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].doNotVerifyRemoteGateways), createObject('value', true()))]", + "useRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'useRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].useRemoteGateways), createObject('value', false()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "17624189975510507274" + }, + "name": "Virtual Network Peerings", + "description": "This module deploys a Virtual Network Peering.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[format('{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkId'), '/')))]", + "metadata": { + "description": "Optional. The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + } + }, + "localVnetName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." + } + }, + "remoteVirtualNetworkId": { + "type": "string", + "metadata": { + "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." + } + }, + "allowForwardedTraffic": { + "type": "bool", + "defaultValue": true, + "metadata": { + "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." + } + }, + "allowGatewayTransit": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "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." + } + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. If we need to verify the provisioning state of the remote gateway. Default is true." + } + }, + "useRemoteGateways": { + "type": "bool", + "defaultValue": false, + "metadata": { + "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." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", + "properties": { + "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", + "allowGatewayTransit": "[parameters('allowGatewayTransit')]", + "allowVirtualNetworkAccess": "[parameters('allowVirtualNetworkAccess')]", + "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", + "useRemoteGateways": "[parameters('useRemoteGateways')]", + "remoteVirtualNetwork": { + "id": "[parameters('remoteVirtualNetworkId')]" + } + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/virtualNetworkPeerings', parameters('localVnetName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_peering_remote": { + "copy": { + "name": "virtualNetwork_peering_remote", + "count": "[length(parameters('peerings'))]" + }, + "condition": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringEnabled'), equals(parameters('peerings')[copyIndex()].remotePeeringEnabled, true()), false())]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-virtualNetworkPeering-remote-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')[2]]", + "resourceGroup": "[split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "localVnetName": { + "value": "[last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/'))]" + }, + "remoteVirtualNetworkId": { + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" + }, + "name": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringName'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringName), createObject('value', format('{0}-{1}', last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')), parameters('name'))))]", + "allowForwardedTraffic": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowForwardedTraffic'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowForwardedTraffic), createObject('value', true()))]", + "allowGatewayTransit": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowGatewayTransit'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowGatewayTransit), createObject('value', false()))]", + "allowVirtualNetworkAccess": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowVirtualNetworkAccess'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowVirtualNetworkAccess), createObject('value', true()))]", + "doNotVerifyRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringDoNotVerifyRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringDoNotVerifyRemoteGateways), createObject('value', true()))]", + "useRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringUseRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringUseRemoteGateways), createObject('value', false()))]" + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.26.170.59819", + "templateHash": "17624189975510507274" + }, + "name": "Virtual Network Peerings", + "description": "This module deploys a Virtual Network Peering.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[format('{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkId'), '/')))]", + "metadata": { + "description": "Optional. The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + } + }, + "localVnetName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." + } + }, + "remoteVirtualNetworkId": { + "type": "string", + "metadata": { + "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." + } + }, + "allowForwardedTraffic": { + "type": "bool", + "defaultValue": true, + "metadata": { + "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." + } + }, + "allowGatewayTransit": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "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." + } + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. If we need to verify the provisioning state of the remote gateway. Default is true." + } + }, + "useRemoteGateways": { + "type": "bool", + "defaultValue": false, + "metadata": { + "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." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", + "properties": { + "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", + "allowGatewayTransit": "[parameters('allowGatewayTransit')]", + "allowVirtualNetworkAccess": "[parameters('allowVirtualNetworkAccess')]", + "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", + "useRemoteGateways": "[parameters('useRemoteGateways')]", + "remoteVirtualNetwork": { + "id": "[parameters('remoteVirtualNetworkId')]" + } + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/virtualNetworkPeerings', parameters('localVnetName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network." + }, + "value": "[parameters('name')]" + }, + "subnetNames": { + "type": "array", + "metadata": { + "description": "The names of the deployed subnets." + }, + "copy": { + "count": "[length(parameters('subnets'))]", + "input": "[parameters('subnets')[copyIndex()].name]" + } + }, + "subnetResourceIds": { + "type": "array", + "metadata": { + "description": "The resource IDs of the deployed subnets." + }, + "copy": { + "count": "[length(parameters('subnets'))]", + "input": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('name'), parameters('subnets')[copyIndex()].name)]" + } + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('virtualNetwork', '2023-04-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "rg" + ] + }, + "assetsStorageAccount": { + "condition": "[or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only base'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-files-sa', deployment().name)]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('assetsStorageAccountName')]" + }, + "allowSharedKeyAccess": { + "value": false + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "networkAcls": { + "value": { + "defaultAction": "Allow" + } + }, + "blobServices": { + "value": { + "containers": [ + { + "name": "[parameters('assetsStorageAccountContainerName')]", + "publicAccess": "None", + "roleAssignments": [ + { + "roleDefinitionIdOrName": "Storage Blob Data Contributor", + "principalId": "[if(or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only base')), reference('dsMsi').outputs.principalId.value, '')]", + "principalType": "ServicePrincipal" + }, + { + "roleDefinitionIdOrName": "Storage Blob Data Reader", + "principalId": "[if(or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only base')), reference('imageMSI').outputs.principalId.value, '')]", + "principalType": "ServicePrincipal" + } + ] + } + ], + "containerDeleteRetentionPolicyEnabled": true, + "containerDeleteRetentionPolicyDays": 10 + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "3958760216991467865" + }, + "name": "Storage Accounts", + "description": "This module deploys a Storage Account.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "networkAclsType": { + "type": "object", + "properties": { + "resourceAccessRules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "tenantId": { + "type": "string", + "metadata": { + "description": "Required. The ID of the tenant in which the resource resides in." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the target service. Can also contain a wildcard, if multiple services e.g. in a resource group should be included." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Sets the resource access rules. Array entries must consist of \"tenantId\" and \"resourceId\" fields only." + } + }, + "bypass": { + "type": "string", + "allowedValues": [ + "AzureServices", + "AzureServices, Logging", + "AzureServices, Logging, Metrics", + "AzureServices, Metrics", + "Logging", + "Logging, Metrics", + "Metrics", + "None" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies whether traffic is bypassed for Logging/Metrics/AzureServices. Possible values are any combination of Logging,Metrics,AzureServices (For example, \"Logging, Metrics\"), or None to bypass none of those traffics." + } + }, + "virtualNetworkRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Sets the virtual network rules." + } + }, + "ipRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Sets the IP ACL rules." + } + }, + "defaultAction": { + "type": "string", + "allowedValues": [ + "Allow", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the default action of allow or deny when no other rules match." + } + } + } + }, + "privateEndpointType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "service": { + "type": "string", + "metadata": { + "description": "Required. The service (sub-) type to deploy the private endpoint for. For example \"vault\" or \"blob\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Manual PrivateLink Service Connections." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint ip address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private ip addresses of the private endpoint." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private ip address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. If used must also be specified in `managedIdentities.userAssignedResourceIds`. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Required. Name of the Storage Account. Must be lower-case." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "kind": { + "type": "string", + "defaultValue": "StorageV2", + "allowedValues": [ + "Storage", + "StorageV2", + "BlobStorage", + "FileStorage", + "BlockBlobStorage" + ], + "metadata": { + "description": "Optional. Type of Storage Account to create." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Standard_GRS", + "allowedValues": [ + "Standard_LRS", + "Standard_GRS", + "Standard_RAGRS", + "Standard_ZRS", + "Premium_LRS", + "Premium_ZRS", + "Standard_GZRS", + "Standard_RAGZRS" + ], + "metadata": { + "description": "Optional. Storage Account Sku Name." + } + }, + "accessTier": { + "type": "string", + "defaultValue": "Hot", + "allowedValues": [ + "Premium", + "Hot", + "Cool" + ], + "metadata": { + "description": "Conditional. Required if the Storage Account kind is set to BlobStorage. The access tier is used for billing. The \"Premium\" access tier is the default value for premium block blobs storage account type and it cannot be changed for the premium block blobs storage account type." + } + }, + "largeFileSharesState": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Allow large file shares if sets to 'Enabled'. It cannot be disabled once it is enabled. Only supported on locally redundant and zone redundant file shares. It cannot be set on FileStorage storage accounts (storage accounts for premium file shares)." + } + }, + "azureFilesIdentityBasedAuthentication": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Provides the identity based authentication settings for Azure Files." + } + }, + "defaultToOAuthAuthentication": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. A boolean flag which indicates whether the default authentication is OAuth or not." + } + }, + "allowSharedKeyAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether the storage account permits requests to be authorized with the account access key via Shared Key. If false, then all requests, including shared access signatures, must be authorized with Azure Active Directory (Azure AD). The default value is null, which is equivalent to true." + } + }, + "privateEndpoints": { + "$ref": "#/definitions/privateEndpointType", + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "managementPolicyRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The Storage Account ManagementPolicies Rules." + } + }, + "networkAcls": { + "$ref": "#/definitions/networkAclsType", + "nullable": true, + "metadata": { + "description": "Optional. Networks ACLs, this value contains IPs to whitelist and/or Subnet information. If in use, bypass needs to be supplied. For security reasons, it is recommended to set the DefaultAction Deny." + } + }, + "requireInfrastructureEncryption": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. A Boolean indicating whether or not the service applies a secondary layer of encryption with platform managed keys for data at rest. For security reasons, it is recommended to set it to true." + } + }, + "allowCrossTenantReplication": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allow or disallow cross AAD tenant object replication." + } + }, + "customDomainName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Sets the custom domain name assigned to the storage account. Name is the CNAME source." + } + }, + "customDomainUseSubDomainName": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether indirect CName validation is enabled. This should only be set on updates." + } + }, + "dnsEndpointType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "AzureDnsZone", + "Standard" + ], + "metadata": { + "description": "Optional. Allows you to specify the type of endpoint. Set this to AzureDNSZone to create a large number of accounts in a single subscription, which creates accounts in an Azure DNS Zone and the endpoint URL will have an alphanumeric DNS Zone identifier." + } + }, + "blobServices": { + "type": "object", + "defaultValue": "[if(not(equals(parameters('kind'), 'FileStorage')), createObject('containerDeleteRetentionPolicyEnabled', true(), 'containerDeleteRetentionPolicyDays', 7, 'deleteRetentionPolicyEnabled', true(), 'deleteRetentionPolicyDays', 6), createObject())]", + "metadata": { + "description": "Optional. Blob service and containers to deploy." + } + }, + "fileServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. File service and shares to deploy." + } + }, + "queueServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Queue service and queues to create." + } + }, + "tableServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Table service and tables to create." + } + }, + "allowBlobPublicAccess": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether public access is enabled for all blobs or containers in the storage account. For security reasons, it is recommended to set it to false." + } + }, + "minimumTlsVersion": { + "type": "string", + "defaultValue": "TLS1_2", + "allowedValues": [ + "TLS1_0", + "TLS1_1", + "TLS1_2" + ], + "metadata": { + "description": "Optional. Set the minimum TLS version on request to storage." + } + }, + "enableHierarchicalNamespace": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Conditional. If true, enables Hierarchical Namespace for the storage account. Required if enableSftp or enableNfsV3 is set to true." + } + }, + "enableSftp": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If true, enables Secure File Transfer Protocol for the storage account. Requires enableHierarchicalNamespace to be true." + } + }, + "localUsers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Local users to deploy for SFTP authentication." + } + }, + "isLocalUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables local users feature, if set to true." + } + }, + "enableNfsV3": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If true, enables NFS 3.0 support for the storage account. Requires enableHierarchicalNamespace to be true." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "allowedCopyScope": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "AAD", + "PrivateLink" + ], + "metadata": { + "description": "Optional. Restrict copy to and from Storage Accounts within an AAD tenant or with Private Links to the same VNet." + } + }, + "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 and networkAcls are not set." + } + }, + "supportsHttpsTrafficOnly": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allows HTTPS traffic only to storage service if sets to true." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "sasExpirationPeriod": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The SAS expiration period. DD.HH:MM:SS." + } + }, + "keyType": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Account", + "Service" + ], + "metadata": { + "description": "Optional. The keyType to use with Queue & Table services." + } + } + }, + "variables": { + "supportsBlobService": "[or(or(or(equals(parameters('kind'), 'BlockBlobStorage'), equals(parameters('kind'), 'BlobStorage')), equals(parameters('kind'), 'StorageV2')), equals(parameters('kind'), 'Storage'))]", + "supportsFileService": "[or(or(equals(parameters('kind'), 'FileStorage'), equals(parameters('kind'), 'StorageV2')), equals(parameters('kind'), 'Storage'))]", + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "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')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Blob Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "Storage Blob Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", + "Storage Blob Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')]", + "Storage Blob Delegator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a')]", + "Storage File Data SMB Share Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb')]", + "Storage File Data SMB Share Elevated Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7')]", + "Storage File Data SMB Share Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314')]", + "Storage Queue Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]", + "Storage Queue Data Message Processor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]", + "Storage Queue Data Message Sender": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a')]", + "Storage Queue Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925')]", + "Storage Table Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]", + "Storage Table Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76199698-9eea-4c19-bc75-cec21354c6b6')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", + "dependsOn": [ + "cMKKeyVault" + ] + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.storage-storageaccount.{0}.{1}', replace('0.9.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + }, + "storageAccount": { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "kind": "[parameters('kind')]", + "sku": { + "name": "[parameters('skuName')]" + }, + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "properties": { + "allowSharedKeyAccess": "[parameters('allowSharedKeyAccess')]", + "defaultToOAuthAuthentication": "[parameters('defaultToOAuthAuthentication')]", + "allowCrossTenantReplication": "[parameters('allowCrossTenantReplication')]", + "allowedCopyScope": "[if(not(empty(parameters('allowedCopyScope'))), parameters('allowedCopyScope'), null())]", + "customDomain": { + "name": "[parameters('customDomainName')]", + "useSubDomainName": "[parameters('customDomainUseSubDomainName')]" + }, + "dnsEndpointType": "[if(not(empty(parameters('dnsEndpointType'))), parameters('dnsEndpointType'), null())]", + "isLocalUserEnabled": "[parameters('isLocalUserEnabled')]", + "encryption": "[union(createObject('keySource', if(not(empty(parameters('customerManagedKey'))), 'Microsoft.Keyvault', 'Microsoft.Storage'), 'services', createObject('blob', if(variables('supportsBlobService'), createObject('enabled', true()), null()), 'file', if(variables('supportsFileService'), createObject('enabled', true()), null()), 'table', createObject('enabled', true(), 'keyType', parameters('keyType')), 'queue', createObject('enabled', true(), 'keyType', parameters('keyType'))), 'keyvaultproperties', if(not(empty(parameters('customerManagedKey'))), createObject('keyname', parameters('customerManagedKey').keyName, 'keyvaulturi', reference('cMKKeyVault').vaultUri, 'keyversion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/')))), null()), 'identity', createObject('userAssignedIdentity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))), null()))), if(parameters('requireInfrastructureEncryption'), createObject('requireInfrastructureEncryption', if(not(equals(parameters('kind'), 'Storage')), parameters('requireInfrastructureEncryption'), null())), createObject()))]", + "accessTier": "[if(and(not(equals(parameters('kind'), 'Storage')), not(equals(parameters('kind'), 'BlockBlobStorage'))), parameters('accessTier'), null())]", + "sasPolicy": "[if(not(empty(parameters('sasExpirationPeriod'))), createObject('expirationAction', 'Log', 'sasExpirationPeriod', parameters('sasExpirationPeriod')), null())]", + "supportsHttpsTrafficOnly": "[parameters('supportsHttpsTrafficOnly')]", + "isHnsEnabled": "[if(parameters('enableHierarchicalNamespace'), parameters('enableHierarchicalNamespace'), null())]", + "isSftpEnabled": "[parameters('enableSftp')]", + "isNfsV3Enabled": "[if(parameters('enableNfsV3'), parameters('enableNfsV3'), '')]", + "largeFileSharesState": "[if(or(equals(parameters('skuName'), 'Standard_LRS'), equals(parameters('skuName'), 'Standard_ZRS')), parameters('largeFileSharesState'), null())]", + "minimumTlsVersion": "[parameters('minimumTlsVersion')]", + "networkAcls": "[if(not(empty(parameters('networkAcls'))), union(createObject('resourceAccessRules', tryGet(parameters('networkAcls'), 'resourceAccessRules'), 'defaultAction', coalesce(tryGet(parameters('networkAcls'), 'defaultAction'), 'Deny'), 'virtualNetworkRules', tryGet(parameters('networkAcls'), 'virtualNetworkRules'), 'ipRules', tryGet(parameters('networkAcls'), 'ipRules')), if(contains(parameters('networkAcls'), 'bypass'), createObject('bypass', tryGet(parameters('networkAcls'), 'bypass')), createObject())), createObject('bypass', 'AzureServices', 'defaultAction', 'Deny'))]", + "allowBlobPublicAccess": "[parameters('allowBlobPublicAccess')]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkAcls'))), 'Disabled', null()))]", + "azureFilesIdentityBasedAuthentication": "[if(not(empty(parameters('azureFilesIdentityBasedAuthentication'))), parameters('azureFilesIdentityBasedAuthentication'), null())]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKUserAssignedIdentity" + ] + }, + "storageAccount_diagnosticSettings": { + "copy": { + "name": "storageAccount_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_roleAssignments": { + "copy": { + "name": "storageAccount_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_privateEndpoints": { + "copy": { + "name": "storageAccount_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-StorageAccount-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroupName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" + }, + "privateDnsZoneResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "4120048060064073955" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "ipConfigurationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "manualPrivateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "$ref": "#/definitions/ipConfigurationsType", + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + } + }, + "privateLinkServiceConnections": { + "$ref": "#/definitions/privateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneResourceIds')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" + }, + "privateDNSResourceIds": { + "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11244630631275470040" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_managementPolicies": { + "condition": "[not(empty(coalesce(parameters('managementPolicyRules'), createArray())))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-ManagementPolicies', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "rules": { + "value": "[coalesce(parameters('managementPolicyRules'), createArray())]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "9473195527943694039" + }, + "name": "Storage Account Management Policies", + "description": "This module deploys a Storage Account Management Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "rules": { + "type": "array", + "metadata": { + "description": "Required. The Storage Account ManagementPolicies Rules." + } + } + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts/managementPolicies", + "apiVersion": "2023-01-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "properties": { + "policy": { + "rules": "[parameters('rules')]" + } + } + } + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed management policy." + }, + "value": "default" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed management policy." + }, + "value": "default" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed management policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount", + "storageAccount_blobServices" + ] + }, + "storageAccount_localUsers": { + "copy": { + "name": "storageAccount_localUsers", + "count": "[length(parameters('localUsers'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-LocalUsers-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('localUsers')[copyIndex()].name]" + }, + "hasSshKey": { + "value": "[parameters('localUsers')[copyIndex()].hasSshKey]" + }, + "hasSshPassword": { + "value": "[parameters('localUsers')[copyIndex()].hasSshPassword]" + }, + "permissionScopes": { + "value": "[parameters('localUsers')[copyIndex()].permissionScopes]" + }, + "hasSharedKey": { + "value": "[tryGet(parameters('localUsers')[copyIndex()], 'hasSharedKey')]" + }, + "homeDirectory": { + "value": "[tryGet(parameters('localUsers')[copyIndex()], 'homeDirectory')]" + }, + "sshAuthorizedKeys": { + "value": "[tryGet(parameters('localUsers')[copyIndex()], 'sshAuthorizedKeys')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "14968464858285923305" + }, + "name": "Storage Account Local Users", + "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "sshAuthorizedKeysType": { + "type": "secureObject", + "properties": { + "secureList": { + "type": "array", + "items": { + "type": "object", + "properties": { + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description used to store the function/usage of the key." + } + }, + "key": { + "type": "string", + "metadata": { + "description": "Required. SSH public key base64 encoded. The format should be: '{keyType} {keyData}', e.g. ssh-rsa AAAABBBB." + } + } + } + }, + "metadata": { + "description": "Optional. The list of SSH authorized keys." + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the local user used for SFTP Authentication." + } + }, + "hasSharedKey": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether shared key exists. Set it to false to remove existing shared key." + } + }, + "hasSshKey": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether SSH key exists. Set it to false to remove existing SSH key." + } + }, + "hasSshPassword": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether SSH password exists. Set it to false to remove existing SSH password." + } + }, + "homeDirectory": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The local user home directory." + } + }, + "permissionScopes": { + "type": "array", + "metadata": { + "description": "Required. The permission scopes of the local user." + } + }, + "sshAuthorizedKeys": { + "$ref": "#/definitions/sshAuthorizedKeysType", + "metadata": { + "description": "Optional. The local user SSH authorized keys for SFTP." + } + } + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "localUsers": { + "type": "Microsoft.Storage/storageAccounts/localUsers", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]", + "properties": { + "hasSharedKey": "[parameters('hasSharedKey')]", + "hasSshKey": "[parameters('hasSshKey')]", + "hasSshPassword": "[parameters('hasSshPassword')]", + "homeDirectory": "[parameters('homeDirectory')]", + "permissionScopes": "[parameters('permissionScopes')]", + "sshAuthorizedKeys": "[tryGet(parameters('sshAuthorizedKeys'), 'secureList')]" + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed local user." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed local user." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed local user." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/localUsers', parameters('storageAccountName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_blobServices": { + "condition": "[not(empty(parameters('blobServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-BlobServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "containers": { + "value": "[tryGet(parameters('blobServices'), 'containers')]" + }, + "automaticSnapshotPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'automaticSnapshotPolicyEnabled')]" + }, + "changeFeedEnabled": { + "value": "[tryGet(parameters('blobServices'), 'changeFeedEnabled')]" + }, + "changeFeedRetentionInDays": { + "value": "[tryGet(parameters('blobServices'), 'changeFeedRetentionInDays')]" + }, + "containerDeleteRetentionPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyEnabled')]" + }, + "containerDeleteRetentionPolicyDays": { + "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyDays')]" + }, + "containerDeleteRetentionPolicyAllowPermanentDelete": { + "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyAllowPermanentDelete')]" + }, + "corsRules": { + "value": "[tryGet(parameters('blobServices'), 'corsRules')]" + }, + "defaultServiceVersion": { + "value": "[tryGet(parameters('blobServices'), 'defaultServiceVersion')]" + }, + "deleteRetentionPolicyAllowPermanentDelete": { + "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyAllowPermanentDelete')]" + }, + "deleteRetentionPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyEnabled')]" + }, + "deleteRetentionPolicyDays": { + "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyDays')]" + }, + "isVersioningEnabled": { + "value": "[tryGet(parameters('blobServices'), 'isVersioningEnabled')]" + }, + "lastAccessTimeTrackingPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'lastAccessTimeTrackingPolicyEnabled')]" + }, + "restorePolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'restorePolicyEnabled')]" + }, + "restorePolicyDays": { + "value": "[tryGet(parameters('blobServices'), 'restorePolicyDays')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('blobServices'), 'diagnosticSettings')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "2306287879023715578" + }, + "name": "Storage Account blob Services", + "description": "This module deploys a Storage Account Blob Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "automaticSnapshotPolicyEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Automatic Snapshot is enabled if set to true." + } + }, + "changeFeedEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The blob service properties for change feed events. Indicates whether change feed event logging is enabled for the Blob service." + } + }, + "changeFeedRetentionInDays": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 146000, + "metadata": { + "description": "Optional. Indicates whether change feed event logging is enabled for the Blob service. Indicates the duration of changeFeed retention in days. If left blank, it indicates an infinite retention of the change feed." + } + }, + "containerDeleteRetentionPolicyEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The blob service properties for container soft delete. Indicates whether DeleteRetentionPolicy is enabled." + } + }, + "containerDeleteRetentionPolicyDays": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 365, + "metadata": { + "description": "Optional. Indicates the number of days that the deleted item should be retained." + } + }, + "containerDeleteRetentionPolicyAllowPermanentDelete": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. This property when set to true allows deletion of the soft deleted blob versions and snapshots. This property cannot be used with blob restore policy. This property only applies to blob service and does not apply to containers or file share." + } + }, + "corsRules": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies CORS rules for the Blob service. You can include up to five CorsRule elements in the request. If no CorsRule elements are included in the request body, all CORS rules will be deleted, and CORS will be disabled for the Blob service." + } + }, + "defaultServiceVersion": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Indicates the default version to use for requests to the Blob service if an incoming request's version is not specified. Possible values include version 2008-10-27 and all more recent versions." + } + }, + "deleteRetentionPolicyEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The blob service properties for blob soft delete." + } + }, + "deleteRetentionPolicyDays": { + "type": "int", + "defaultValue": 7, + "minValue": 1, + "maxValue": 365, + "metadata": { + "description": "Optional. Indicates the number of days that the deleted blob should be retained." + } + }, + "deleteRetentionPolicyAllowPermanentDelete": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. This property when set to true allows deletion of the soft deleted blob versions and snapshots. This property cannot be used with blob restore policy. This property only applies to blob service and does not apply to containers or file share." + } + }, + "isVersioningEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Use versioning to automatically maintain previous versions of your blobs." + } + }, + "lastAccessTimeTrackingPolicyEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The blob service property to configure last access time based tracking policy. When set to true last access time based tracking is enabled." + } + }, + "restorePolicyEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The blob service properties for blob restore policy. If point-in-time restore is enabled, then versioning, change feed, and blob soft delete must also be enabled." + } + }, + "restorePolicyDays": { + "type": "int", + "defaultValue": 6, + "minValue": 1, + "metadata": { + "description": "Optional. How long this blob can be restored. It should be less than DeleteRetentionPolicy days." + } + }, + "containers": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Blob containers to create." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "name": "default" + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[parameters('storageAccountName')]" + }, + "blobServices": { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "properties": { + "automaticSnapshotPolicyEnabled": "[parameters('automaticSnapshotPolicyEnabled')]", + "changeFeed": "[if(parameters('changeFeedEnabled'), createObject('enabled', true(), 'retentionInDays', parameters('changeFeedRetentionInDays')), null())]", + "containerDeleteRetentionPolicy": { + "enabled": "[parameters('containerDeleteRetentionPolicyEnabled')]", + "days": "[parameters('containerDeleteRetentionPolicyDays')]", + "allowPermanentDelete": "[if(equals(parameters('containerDeleteRetentionPolicyEnabled'), true()), parameters('containerDeleteRetentionPolicyAllowPermanentDelete'), null())]" + }, + "cors": { + "corsRules": "[parameters('corsRules')]" + }, + "defaultServiceVersion": "[if(not(empty(parameters('defaultServiceVersion'))), parameters('defaultServiceVersion'), null())]", + "deleteRetentionPolicy": { + "enabled": "[parameters('deleteRetentionPolicyEnabled')]", + "days": "[parameters('deleteRetentionPolicyDays')]", + "allowPermanentDelete": "[if(and(parameters('deleteRetentionPolicyEnabled'), parameters('deleteRetentionPolicyAllowPermanentDelete')), true(), null())]" + }, + "isVersioningEnabled": "[parameters('isVersioningEnabled')]", + "lastAccessTimeTrackingPolicy": "[if(not(equals(reference('storageAccount', '2022-09-01', 'full').kind, 'Storage')), createObject('enable', parameters('lastAccessTimeTrackingPolicyEnabled'), 'name', if(equals(parameters('lastAccessTimeTrackingPolicyEnabled'), true()), 'AccessTimeTracking', null()), 'trackingGranularityInDays', if(equals(parameters('lastAccessTimeTrackingPolicyEnabled'), true()), 1, null())), null())]", + "restorePolicy": "[if(parameters('restorePolicyEnabled'), createObject('enabled', true(), 'days', parameters('restorePolicyDays')), null())]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "blobServices_diagnosticSettings": { + "copy": { + "name": "blobServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}', parameters('storageAccountName'), variables('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "blobServices" + ] + }, + "blobServices_container": { + "copy": { + "name": "blobServices_container", + "count": "[length(coalesce(parameters('containers'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Container-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "name": { + "value": "[coalesce(parameters('containers'), createArray())[copyIndex()].name]" + }, + "defaultEncryptionScope": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'defaultEncryptionScope')]" + }, + "denyEncryptionScopeOverride": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'denyEncryptionScopeOverride')]" + }, + "enableNfsV3AllSquash": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'enableNfsV3AllSquash')]" + }, + "enableNfsV3RootSquash": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'enableNfsV3RootSquash')]" + }, + "immutableStorageWithVersioningEnabled": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'immutableStorageWithVersioningEnabled')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'metadata')]" + }, + "publicAccess": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'publicAccess')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "immutabilityPolicyProperties": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'immutabilityPolicyProperties')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "7045309160947869799" + }, + "name": "Storage Account Blob Containers", + "description": "This module deploys a Storage Account Blob Container.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the storage container to deploy." + } + }, + "defaultEncryptionScope": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Default the container to use specified encryption scope for all writes." + } + }, + "denyEncryptionScopeOverride": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Block override of encryption scope from the container default." + } + }, + "enableNfsV3AllSquash": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable NFSv3 all squash on blob container." + } + }, + "enableNfsV3RootSquash": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable NFSv3 root squash on blob container." + } + }, + "immutableStorageWithVersioningEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. This is an immutable property, when set to true it enables object level immutability at the container level. The property is immutable and can only be set to true at the container creation time. Existing containers must undergo a migration process." + } + }, + "immutabilityPolicyName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. Name of the immutable policy." + } + }, + "immutabilityPolicyProperties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Configure immutability policy." + } + }, + "metadata": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. A name-value pair to associate with the container as metadata." + } + }, + "publicAccess": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "Container", + "Blob", + "None" + ], + "metadata": { + "description": "Optional. Specifies whether data in the container may be accessed publicly and the level of access." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "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')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Blob Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "Storage Blob Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", + "Storage Blob Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')]", + "Storage Blob Delegator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::blobServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[parameters('storageAccountName')]" + }, + "container": { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "properties": { + "defaultEncryptionScope": "[if(not(empty(parameters('defaultEncryptionScope'))), parameters('defaultEncryptionScope'), null())]", + "denyEncryptionScopeOverride": "[if(equals(parameters('denyEncryptionScopeOverride'), true()), parameters('denyEncryptionScopeOverride'), null())]", + "enableNfsV3AllSquash": "[if(equals(parameters('enableNfsV3AllSquash'), true()), parameters('enableNfsV3AllSquash'), null())]", + "enableNfsV3RootSquash": "[if(equals(parameters('enableNfsV3RootSquash'), true()), parameters('enableNfsV3RootSquash'), null())]", + "immutableStorageWithVersioning": "[if(equals(parameters('immutableStorageWithVersioningEnabled'), true()), createObject('enabled', parameters('immutableStorageWithVersioningEnabled')), null())]", + "metadata": "[parameters('metadata')]", + "publicAccess": "[parameters('publicAccess')]" + }, + "dependsOn": [ + "storageAccount::blobServices" + ] + }, + "container_roleAssignments": { + "copy": { + "name": "container_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}/containers/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "container" + ] + }, + "immutabilityPolicy": { + "condition": "[not(empty(coalesce(parameters('immutabilityPolicyProperties'), createObject())))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[parameters('immutabilityPolicyName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "containerName": { + "value": "[parameters('name')]" + }, + "immutabilityPeriodSinceCreationInDays": { + "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'immutabilityPeriodSinceCreationInDays')]" + }, + "allowProtectedAppendWrites": { + "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'allowProtectedAppendWrites')]" + }, + "allowProtectedAppendWritesAll": { + "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'allowProtectedAppendWritesAll')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "2543276032744560941" + }, + "name": "Storage Account Blob Container Immutability Policies", + "description": "This module deploys a Storage Account Blob Container Immutability Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "containerName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent container to apply the policy to. Required if the template is used in a standalone deployment." + } + }, + "immutabilityPeriodSinceCreationInDays": { + "type": "int", + "defaultValue": 365, + "metadata": { + "description": "Optional. The immutability period for the blobs in the container since the policy creation, in days." + } + }, + "allowProtectedAppendWrites": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to an append blob while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API." + } + }, + "allowProtectedAppendWritesAll": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to both \"Append and Block Blobs\" while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API. The \"allowProtectedAppendWrites\" and \"allowProtectedAppendWritesAll\" properties are mutually exclusive." + } + } + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}/{2}/{3}', parameters('storageAccountName'), 'default', parameters('containerName'), 'default')]", + "properties": { + "immutabilityPeriodSinceCreationInDays": "[parameters('immutabilityPeriodSinceCreationInDays')]", + "allowProtectedAppendWrites": "[parameters('allowProtectedAppendWrites')]", + "allowProtectedAppendWritesAll": "[parameters('allowProtectedAppendWritesAll')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed immutability policy." + }, + "value": "default" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed immutability policy." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies', parameters('storageAccountName'), 'default', parameters('containerName'), 'default')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed immutability policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "container", + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed container." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed container." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed container." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed blob service." + }, + "value": "[variables('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed blob service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccountName'), variables('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the deployed blob service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_fileServices": { + "condition": "[not(empty(parameters('fileServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-FileServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('fileServices'), 'diagnosticSettings')]" + }, + "protocolSettings": { + "value": "[tryGet(parameters('fileServices'), 'protocolSettings')]" + }, + "shareDeleteRetentionPolicy": { + "value": "[tryGet(parameters('fileServices'), 'shareDeleteRetentionPolicy')]" + }, + "shares": { + "value": "[tryGet(parameters('fileServices'), 'shares')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "7463227074634701879" + }, + "name": "Storage Account File Share Services", + "description": "This module deploys a Storage Account File Share Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the file service." + } + }, + "protocolSettings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Protocol settings for file service." + } + }, + "shareDeleteRetentionPolicy": { + "type": "object", + "defaultValue": { + "enabled": true, + "days": 7 + }, + "metadata": { + "description": "Optional. The service properties for soft delete." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "shares": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. File shares to create." + } + } + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "fileServices": { + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]", + "properties": { + "protocolSettings": "[parameters('protocolSettings')]", + "shareDeleteRetentionPolicy": "[parameters('shareDeleteRetentionPolicy')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "fileServices_diagnosticSettings": { + "copy": { + "name": "fileServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/fileServices/{1}', parameters('storageAccountName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "fileServices" + ] + }, + "fileServices_shares": { + "copy": { + "name": "fileServices_shares", + "count": "[length(coalesce(parameters('shares'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-shares-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "fileServicesName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('shares'), createArray())[copyIndex()].name]" + }, + "accessTier": { + "value": "[coalesce(tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'accessTier'), if(equals(reference('storageAccount', '2023-04-01', 'full').kind, 'FileStorage'), 'Premium', 'TransactionOptimized'))]" + }, + "enabledProtocols": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'enabledProtocols')]" + }, + "rootSquash": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'rootSquash')]" + }, + "shareQuota": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'shareQuota')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "1342480740201032357" + }, + "name": "Storage Account File Shares", + "description": "This module deploys a Storage Account File Share.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "fileServicesName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Conditional. The name of the parent file service. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the file share to create." + } + }, + "accessTier": { + "type": "string", + "defaultValue": "TransactionOptimized", + "allowedValues": [ + "Premium", + "Hot", + "Cool", + "TransactionOptimized" + ], + "metadata": { + "description": "Conditional. Access tier for specific share. Required if the Storage Account kind is set to FileStorage (should be set to \"Premium\"). GpV2 account can choose between TransactionOptimized (default), Hot, and Cool." + } + }, + "shareQuota": { + "type": "int", + "defaultValue": 5120, + "metadata": { + "description": "Optional. The maximum size of the share, in gigabytes. Must be greater than 0, and less than or equal to 5120 (5TB). For Large File Shares, the maximum size is 102400 (100TB)." + } + }, + "enabledProtocols": { + "type": "string", + "defaultValue": "SMB", + "allowedValues": [ + "NFS", + "SMB" + ], + "metadata": { + "description": "Optional. The authentication protocol that is used for the file share. Can only be specified when creating a share." + } + }, + "rootSquash": { + "type": "string", + "defaultValue": "NoRootSquash", + "allowedValues": [ + "AllSquash", + "NoRootSquash", + "RootSquash" + ], + "metadata": { + "description": "Optional. Permissions for NFS file shares are enforced by the client OS rather than the Azure Files service. Toggling the root squash behavior reduces the rights of the root user for NFS shares." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "resources": { + "storageAccount::fileService": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('fileServicesName'))]", + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "fileShare": { + "type": "Microsoft.Storage/storageAccounts/fileServices/shares", + "apiVersion": "2023-01-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]", + "properties": { + "accessTier": "[parameters('accessTier')]", + "shareQuota": "[parameters('shareQuota')]", + "rootSquash": "[if(equals(parameters('enabledProtocols'), 'NFS'), parameters('rootSquash'), null())]", + "enabledProtocols": "[parameters('enabledProtocols')]" + }, + "dependsOn": [ + "storageAccount::fileService" + ] + }, + "fileShare_roleAssignments": { + "condition": "[not(empty(parameters('roleAssignments')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Share-Rbac', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "fileShareResourceId": { + "value": "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]" + }, + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "8779226603522513073" + } + }, + "parameters": { + "roleAssignments": { + "type": "array", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "fileShareResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the file share to assign the roles to." + } + } + }, + "variables": { + "$fxv#0": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "scope": { + "type": "string", + "metadata": { + "description": "Required. The scope to deploy the role assignment to." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the role assignment." + } + }, + "roleDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. The role definition Id to assign." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User", + "" + ], + "defaultValue": "", + "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", + "allowedValues": [ + "2.0" + ], + "defaultValue": "2.0", + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[[parameters('scope')]", + "name": "[[parameters('name')]", + "properties": { + "roleDefinitionId": "[[parameters('roleDefinitionId')]", + "principalId": "[[parameters('principalId')]", + "description": "[[parameters('description')]", + "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())]" + } + } + ] + }, + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "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')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage File Data SMB Share Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb')]", + "Storage File Data SMB Share Elevated Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7')]", + "Storage File Data SMB Share Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": [ + { + "copy": { + "name": "fileShare_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('{0}-Share-Rbac-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "mode": "Incremental", + "expressionEvaluationOptions": { + "scope": "Outer" + }, + "template": "[variables('$fxv#0')]", + "parameters": { + "scope": { + "value": "[replace(parameters('fileShareResourceId'), '/shares/', '/fileShares/')]" + }, + "name": { + "value": "[guid(parameters('fileShareResourceId'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, 'tyfa')]" + }, + "roleDefinitionId": { + "value": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]" + }, + "principalId": { + "value": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]" + }, + "principalType": { + "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]" + }, + "condition": { + "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]" + }, + "conditionVersion": { + "value": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]" + }, + "delegatedManagedIdentityResourceId": { + "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + } + } + } + } + ] + } + }, + "dependsOn": [ + "fileShare" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "fileServices", + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share service." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/fileServices', parameters('storageAccountName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_queueServices": { + "condition": "[not(empty(parameters('queueServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-QueueServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('queueServices'), 'diagnosticSettings')]" + }, + "queues": { + "value": "[tryGet(parameters('queueServices'), 'queues')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "10678250016540336570" + }, + "name": "Storage Account Queue Services", + "description": "This module deploys a Storage Account Queue Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "queues": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Queues to create." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "name": "default" + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "queueServices": { + "type": "Microsoft.Storage/storageAccounts/queueServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "properties": {}, + "dependsOn": [ + "storageAccount" + ] + }, + "queueServices_diagnosticSettings": { + "copy": { + "name": "queueServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/queueServices/{1}', parameters('storageAccountName'), variables('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "queueServices" + ] + }, + "queueServices_queues": { + "copy": { + "name": "queueServices_queues", + "count": "[length(coalesce(parameters('queues'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Queue-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "name": { + "value": "[coalesce(parameters('queues'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('queues'), createArray())[copyIndex()], 'metadata')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('queues'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "13487964166280180730" + }, + "name": "Storage Account Queues", + "description": "This module deploys a Storage Account Queue.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the storage queue to deploy." + } + }, + "metadata": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Required. A name-value pair that represents queue metadata." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "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')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Queue Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]", + "Storage Queue Data Message Processor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]", + "Storage Queue Data Message Sender": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a')]", + "Storage Queue Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::queueServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/queueServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "queue": { + "type": "Microsoft.Storage/storageAccounts/queueServices/queues", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]" + }, + "dependsOn": [ + "storageAccount::queueServices" + ] + }, + "queue_roleAssignments": { + "copy": { + "name": "queue_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/queueServices/{1}/queues/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Storage/storageAccounts/queueServices/queues', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "queue" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed queue." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed queue." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/queueServices/queues', parameters('storageAccountName'), 'default', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed queue." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share service." + }, + "value": "[variables('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/queueServices', parameters('storageAccountName'), variables('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_tableServices": { + "condition": "[not(empty(parameters('tableServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-TableServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('tableServices'), 'diagnosticSettings')]" + }, + "tables": { + "value": "[tryGet(parameters('tableServices'), 'tables')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "16839054392438941735" + }, + "name": "Storage Account Table Services", + "description": "This module deploys a Storage Account Table Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "tables": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. tables to create." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "name": "default" + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "tableServices": { + "type": "Microsoft.Storage/storageAccounts/tableServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "properties": {}, + "dependsOn": [ + "storageAccount" + ] + }, + "tableServices_diagnosticSettings": { + "copy": { + "name": "tableServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/tableServices/{1}', parameters('storageAccountName'), variables('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "tableServices" + ] + }, + "tableServices_tables": { + "copy": { + "name": "tableServices_tables", + "count": "[length(parameters('tables'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Table-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('tables')[copyIndex()].name]" + }, + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "roleAssignments": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "3177845984945141330" + }, + "name": "Storage Account Table", + "description": "This module deploys a Storage Account Table.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the table." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "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')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Table Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]", + "Storage Table Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76199698-9eea-4c19-bc75-cec21354c6b6')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::tableServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/tableServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "table": { + "type": "Microsoft.Storage/storageAccounts/tableServices/tables", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "dependsOn": [ + "storageAccount::tableServices" + ] + }, + "table_roleAssignments": { + "copy": { + "name": "table_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/tableServices/{1}/tables/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Storage/storageAccounts/tableServices/tables', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "table" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share service." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/tableServices/tables', parameters('storageAccountName'), 'default', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed table service." + }, + "value": "[variables('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed table service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/tableServices', parameters('storageAccountName'), variables('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed table service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed storage account." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed storage account." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed storage account." + }, + "value": "[resourceGroup().name]" + }, + "primaryBlobEndpoint": { + "type": "string", + "metadata": { + "description": "The primary blob endpoint reference if blob services are deployed." + }, + "value": "[if(and(not(empty(parameters('blobServices'))), contains(parameters('blobServices'), 'containers')), reference(format('Microsoft.Storage/storageAccounts/{0}', parameters('name')), '2019-04-01').primaryEndpoints.blob, '')]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('storageAccount', '2022-09-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('storageAccount', '2022-09-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "dsMsi", + "imageMSI", + "rg" + ] + }, + "dsStorageAccount": { + "condition": "[or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only base'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-ds-sa', deployment().name)]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('deploymentScriptStorageAccountName')]" + }, + "allowSharedKeyAccess": { + "value": true + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "[tenantResourceId('Microsoft.Authorization/roleDefinitions', '69566ab7-960f-475b-8e7c-b3118f30c6bd')]", + "principalId": "[if(or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only base')), reference('dsMsi').outputs.principalId.value, '')]", + "principalType": "ServicePrincipal" + } + ] + }, + "location": { + "value": "[parameters('location')]" + }, + "networkAcls": { + "value": { + "bypass": "AzureServices", + "defaultAction": "Deny", + "virtualNetworkRules": [ + { + "action": "Allow", + "id": "[resourceId(subscription().subscriptionId, parameters('resourceGroupName'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('deploymentScriptSubnetName'))]" + } + ] + } + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "3958760216991467865" + }, + "name": "Storage Accounts", + "description": "This module deploys a Storage Account.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "networkAclsType": { + "type": "object", + "properties": { + "resourceAccessRules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "tenantId": { + "type": "string", + "metadata": { + "description": "Required. The ID of the tenant in which the resource resides in." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the target service. Can also contain a wildcard, if multiple services e.g. in a resource group should be included." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Sets the resource access rules. Array entries must consist of \"tenantId\" and \"resourceId\" fields only." + } + }, + "bypass": { + "type": "string", + "allowedValues": [ + "AzureServices", + "AzureServices, Logging", + "AzureServices, Logging, Metrics", + "AzureServices, Metrics", + "Logging", + "Logging, Metrics", + "Metrics", + "None" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies whether traffic is bypassed for Logging/Metrics/AzureServices. Possible values are any combination of Logging,Metrics,AzureServices (For example, \"Logging, Metrics\"), or None to bypass none of those traffics." + } + }, + "virtualNetworkRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Sets the virtual network rules." + } + }, + "ipRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Sets the IP ACL rules." + } + }, + "defaultAction": { + "type": "string", + "allowedValues": [ + "Allow", + "Deny" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specifies the default action of allow or deny when no other rules match." + } + } + } + }, + "privateEndpointType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "service": { + "type": "string", + "metadata": { + "description": "Required. The service (sub-) type to deploy the private endpoint for. For example \"vault\" or \"blob\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if privateDnsZoneResourceIds were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint with. A DNS zone group can support up to 5 DNS zones." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Manual PrivateLink Service Connections." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint ip address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private ip addresses of the private endpoint." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private ip address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + }, + "customerManagedKeyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + }, + "keyVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The version of the customer managed key to reference for encryption. If not provided, using 'latest'." + } + }, + "userAssignedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. User assigned identity to use when fetching the customer managed key. If used must also be specified in `managedIdentities.userAssignedResourceIds`. Required if no system assigned identity is available for use." + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Required. Name of the Storage Account. Must be lower-case." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "kind": { + "type": "string", + "defaultValue": "StorageV2", + "allowedValues": [ + "Storage", + "StorageV2", + "BlobStorage", + "FileStorage", + "BlockBlobStorage" + ], + "metadata": { + "description": "Optional. Type of Storage Account to create." + } + }, + "skuName": { + "type": "string", + "defaultValue": "Standard_GRS", + "allowedValues": [ + "Standard_LRS", + "Standard_GRS", + "Standard_RAGRS", + "Standard_ZRS", + "Premium_LRS", + "Premium_ZRS", + "Standard_GZRS", + "Standard_RAGZRS" + ], + "metadata": { + "description": "Optional. Storage Account Sku Name." + } + }, + "accessTier": { + "type": "string", + "defaultValue": "Hot", + "allowedValues": [ + "Premium", + "Hot", + "Cool" + ], + "metadata": { + "description": "Conditional. Required if the Storage Account kind is set to BlobStorage. The access tier is used for billing. The \"Premium\" access tier is the default value for premium block blobs storage account type and it cannot be changed for the premium block blobs storage account type." + } + }, + "largeFileSharesState": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Disabled", + "Enabled" + ], + "metadata": { + "description": "Optional. Allow large file shares if sets to 'Enabled'. It cannot be disabled once it is enabled. Only supported on locally redundant and zone redundant file shares. It cannot be set on FileStorage storage accounts (storage accounts for premium file shares)." + } + }, + "azureFilesIdentityBasedAuthentication": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Provides the identity based authentication settings for Azure Files." + } + }, + "defaultToOAuthAuthentication": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. A boolean flag which indicates whether the default authentication is OAuth or not." + } + }, + "allowSharedKeyAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Indicates whether the storage account permits requests to be authorized with the account access key via Shared Key. If false, then all requests, including shared access signatures, must be authorized with Azure Active Directory (Azure AD). The default value is null, which is equivalent to true." + } + }, + "privateEndpoints": { + "$ref": "#/definitions/privateEndpointType", + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "managementPolicyRules": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The Storage Account ManagementPolicies Rules." + } + }, + "networkAcls": { + "$ref": "#/definitions/networkAclsType", + "nullable": true, + "metadata": { + "description": "Optional. Networks ACLs, this value contains IPs to whitelist and/or Subnet information. If in use, bypass needs to be supplied. For security reasons, it is recommended to set the DefaultAction Deny." + } + }, + "requireInfrastructureEncryption": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. A Boolean indicating whether or not the service applies a secondary layer of encryption with platform managed keys for data at rest. For security reasons, it is recommended to set it to true." + } + }, + "allowCrossTenantReplication": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allow or disallow cross AAD tenant object replication." + } + }, + "customDomainName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Sets the custom domain name assigned to the storage account. Name is the CNAME source." + } + }, + "customDomainUseSubDomainName": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether indirect CName validation is enabled. This should only be set on updates." + } + }, + "dnsEndpointType": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "AzureDnsZone", + "Standard" + ], + "metadata": { + "description": "Optional. Allows you to specify the type of endpoint. Set this to AzureDNSZone to create a large number of accounts in a single subscription, which creates accounts in an Azure DNS Zone and the endpoint URL will have an alphanumeric DNS Zone identifier." + } + }, + "blobServices": { + "type": "object", + "defaultValue": "[if(not(equals(parameters('kind'), 'FileStorage')), createObject('containerDeleteRetentionPolicyEnabled', true(), 'containerDeleteRetentionPolicyDays', 7, 'deleteRetentionPolicyEnabled', true(), 'deleteRetentionPolicyDays', 6), createObject())]", + "metadata": { + "description": "Optional. Blob service and containers to deploy." + } + }, + "fileServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. File service and shares to deploy." + } + }, + "queueServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Queue service and queues to create." + } + }, + "tableServices": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Table service and tables to create." + } + }, + "allowBlobPublicAccess": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether public access is enabled for all blobs or containers in the storage account. For security reasons, it is recommended to set it to false." + } + }, + "minimumTlsVersion": { + "type": "string", + "defaultValue": "TLS1_2", + "allowedValues": [ + "TLS1_0", + "TLS1_1", + "TLS1_2" + ], + "metadata": { + "description": "Optional. Set the minimum TLS version on request to storage." + } + }, + "enableHierarchicalNamespace": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Conditional. If true, enables Hierarchical Namespace for the storage account. Required if enableSftp or enableNfsV3 is set to true." + } + }, + "enableSftp": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If true, enables Secure File Transfer Protocol for the storage account. Requires enableHierarchicalNamespace to be true." + } + }, + "localUsers": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Local users to deploy for SFTP authentication." + } + }, + "isLocalUserEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enables local users feature, if set to true." + } + }, + "enableNfsV3": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If true, enables NFS 3.0 support for the storage account. Requires enableHierarchicalNamespace to be true." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "allowedCopyScope": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "", + "AAD", + "PrivateLink" + ], + "metadata": { + "description": "Optional. Restrict copy to and from Storage Accounts within an AAD tenant or with Private Links to the same VNet." + } + }, + "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 and networkAcls are not set." + } + }, + "supportsHttpsTrafficOnly": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Allows HTTPS traffic only to storage service if sets to true." + } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyType", + "metadata": { + "description": "Optional. The customer managed key definition." + } + }, + "sasExpirationPeriod": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The SAS expiration period. DD.HH:MM:SS." + } + }, + "keyType": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Account", + "Service" + ], + "metadata": { + "description": "Optional. The keyType to use with Queue & Table services." + } + } + }, + "variables": { + "supportsBlobService": "[or(or(or(equals(parameters('kind'), 'BlockBlobStorage'), equals(parameters('kind'), 'BlobStorage')), equals(parameters('kind'), 'StorageV2')), equals(parameters('kind'), 'Storage'))]", + "supportsFileService": "[or(or(equals(parameters('kind'), 'FileStorage'), equals(parameters('kind'), 'StorageV2')), equals(parameters('kind'), 'Storage'))]", + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "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')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Blob Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "Storage Blob Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", + "Storage Blob Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')]", + "Storage Blob Delegator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a')]", + "Storage File Data SMB Share Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb')]", + "Storage File Data SMB Share Elevated Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7')]", + "Storage File Data SMB Share Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314')]", + "Storage Queue Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]", + "Storage Queue Data Message Processor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]", + "Storage Queue Data Message Sender": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a')]", + "Storage Queue Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925')]", + "Storage Table Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]", + "Storage Table Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76199698-9eea-4c19-bc75-cec21354c6b6')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), and(not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'))), not(empty(tryGet(parameters('customerManagedKey'), 'keyName')))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[format('{0}/{1}', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/')), coalesce(tryGet(parameters('customerManagedKey'), 'keyName'), 'dummyKey'))]", + "dependsOn": [ + "cMKKeyVault" + ] + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.storage-storageaccount.{0}.{1}', replace('0.9.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "cMKKeyVault": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2023-02-01", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), 'dummyVault'), '/'))]" + }, + "cMKUserAssignedIdentity": { + "condition": "[not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId')))]", + "existing": true, + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "subscriptionId": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]]", + "name": "[last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))]" + }, + "storageAccount": { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "kind": "[parameters('kind')]", + "sku": { + "name": "[parameters('skuName')]" + }, + "identity": "[variables('identity')]", + "tags": "[parameters('tags')]", + "properties": { + "allowSharedKeyAccess": "[parameters('allowSharedKeyAccess')]", + "defaultToOAuthAuthentication": "[parameters('defaultToOAuthAuthentication')]", + "allowCrossTenantReplication": "[parameters('allowCrossTenantReplication')]", + "allowedCopyScope": "[if(not(empty(parameters('allowedCopyScope'))), parameters('allowedCopyScope'), null())]", + "customDomain": { + "name": "[parameters('customDomainName')]", + "useSubDomainName": "[parameters('customDomainUseSubDomainName')]" + }, + "dnsEndpointType": "[if(not(empty(parameters('dnsEndpointType'))), parameters('dnsEndpointType'), null())]", + "isLocalUserEnabled": "[parameters('isLocalUserEnabled')]", + "encryption": "[union(createObject('keySource', if(not(empty(parameters('customerManagedKey'))), 'Microsoft.Keyvault', 'Microsoft.Storage'), 'services', createObject('blob', if(variables('supportsBlobService'), createObject('enabled', true()), null()), 'file', if(variables('supportsFileService'), createObject('enabled', true()), null()), 'table', createObject('enabled', true(), 'keyType', parameters('keyType')), 'queue', createObject('enabled', true(), 'keyType', parameters('keyType'))), 'keyvaultproperties', if(not(empty(parameters('customerManagedKey'))), createObject('keyname', parameters('customerManagedKey').keyName, 'keyvaulturi', reference('cMKKeyVault').vaultUri, 'keyversion', if(not(empty(coalesce(tryGet(parameters('customerManagedKey'), 'keyVersion'), ''))), parameters('customerManagedKey').keyVersion, last(split(reference('cMKKeyVault::cMKKey').keyUriWithVersion, '/')))), null()), 'identity', createObject('userAssignedIdentity', if(not(empty(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'))), extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '//'), '/')[2], split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), '////'), '/')[4]), 'Microsoft.ManagedIdentity/userAssignedIdentities', last(split(coalesce(tryGet(parameters('customerManagedKey'), 'userAssignedIdentityResourceId'), 'dummyMsi'), '/'))), null()))), if(parameters('requireInfrastructureEncryption'), createObject('requireInfrastructureEncryption', if(not(equals(parameters('kind'), 'Storage')), parameters('requireInfrastructureEncryption'), null())), createObject()))]", + "accessTier": "[if(and(not(equals(parameters('kind'), 'Storage')), not(equals(parameters('kind'), 'BlockBlobStorage'))), parameters('accessTier'), null())]", + "sasPolicy": "[if(not(empty(parameters('sasExpirationPeriod'))), createObject('expirationAction', 'Log', 'sasExpirationPeriod', parameters('sasExpirationPeriod')), null())]", + "supportsHttpsTrafficOnly": "[parameters('supportsHttpsTrafficOnly')]", + "isHnsEnabled": "[if(parameters('enableHierarchicalNamespace'), parameters('enableHierarchicalNamespace'), null())]", + "isSftpEnabled": "[parameters('enableSftp')]", + "isNfsV3Enabled": "[if(parameters('enableNfsV3'), parameters('enableNfsV3'), '')]", + "largeFileSharesState": "[if(or(equals(parameters('skuName'), 'Standard_LRS'), equals(parameters('skuName'), 'Standard_ZRS')), parameters('largeFileSharesState'), null())]", + "minimumTlsVersion": "[parameters('minimumTlsVersion')]", + "networkAcls": "[if(not(empty(parameters('networkAcls'))), union(createObject('resourceAccessRules', tryGet(parameters('networkAcls'), 'resourceAccessRules'), 'defaultAction', coalesce(tryGet(parameters('networkAcls'), 'defaultAction'), 'Deny'), 'virtualNetworkRules', tryGet(parameters('networkAcls'), 'virtualNetworkRules'), 'ipRules', tryGet(parameters('networkAcls'), 'ipRules')), if(contains(parameters('networkAcls'), 'bypass'), createObject('bypass', tryGet(parameters('networkAcls'), 'bypass')), createObject())), createObject('bypass', 'AzureServices', 'defaultAction', 'Deny'))]", + "allowBlobPublicAccess": "[parameters('allowBlobPublicAccess')]", + "publicNetworkAccess": "[if(not(empty(parameters('publicNetworkAccess'))), parameters('publicNetworkAccess'), if(and(not(empty(parameters('privateEndpoints'))), empty(parameters('networkAcls'))), 'Disabled', null()))]", + "azureFilesIdentityBasedAuthentication": "[if(not(empty(parameters('azureFilesIdentityBasedAuthentication'))), parameters('azureFilesIdentityBasedAuthentication'), null())]" + }, + "dependsOn": [ + "cMKKeyVault", + "cMKUserAssignedIdentity" + ] + }, + "storageAccount_diagnosticSettings": { + "copy": { + "name": "storageAccount_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_roleAssignments": { + "copy": { + "name": "storageAccount_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_privateEndpoints": { + "copy": { + "name": "storageAccount_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-StorageAccount-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualPrivateLinkServiceConnections'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '/')), coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service, copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.Storage/storageAccounts', parameters('name')), 'groupIds', createArray(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].service), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroupName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroupName')]" + }, + "privateDnsZoneResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneResourceIds')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "4120048060064073955" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "ipConfigurationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "manualPrivateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "$ref": "#/definitions/ipConfigurationsType", + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group to create if `privateDnsZoneResourceIds` were provided." + } + }, + "privateDnsZoneResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + } + }, + "privateLinkServiceConnections": { + "$ref": "#/definitions/privateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2023-07-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.4.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneResourceIds')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(parameters('privateDnsZoneGroupName'), 'default')]" + }, + "privateDNSResourceIds": { + "value": "[coalesce(parameters('privateDnsZoneResourceIds'), createArray())]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.25.53.49325", + "templateHash": "11244630631275470040" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDNSResourceIds": { + "type": "array", + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone resource IDs. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigs", + "count": "[length(parameters('privateDNSResourceIds'))]", + "input": { + "name": "[last(split(parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')], '/'))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDNSResourceIds')[copyIndex('privateDnsZoneConfigs')]]" + } + } + } + ] + }, + "resources": [ + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigs')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-04-01', 'full').location]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties.groupIds[0], reference('privateEndpoint').privateLinkServiceConnections[0].properties.groupIds[0])]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_managementPolicies": { + "condition": "[not(empty(coalesce(parameters('managementPolicyRules'), createArray())))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-ManagementPolicies', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "rules": { + "value": "[coalesce(parameters('managementPolicyRules'), createArray())]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "9473195527943694039" + }, + "name": "Storage Account Management Policies", + "description": "This module deploys a Storage Account Management Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "rules": { + "type": "array", + "metadata": { + "description": "Required. The Storage Account ManagementPolicies Rules." + } + } + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts/managementPolicies", + "apiVersion": "2023-01-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "properties": { + "policy": { + "rules": "[parameters('rules')]" + } + } + } + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed management policy." + }, + "value": "default" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed management policy." + }, + "value": "default" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed management policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount", + "storageAccount_blobServices" + ] + }, + "storageAccount_localUsers": { + "copy": { + "name": "storageAccount_localUsers", + "count": "[length(parameters('localUsers'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-LocalUsers-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[parameters('localUsers')[copyIndex()].name]" + }, + "hasSshKey": { + "value": "[parameters('localUsers')[copyIndex()].hasSshKey]" + }, + "hasSshPassword": { + "value": "[parameters('localUsers')[copyIndex()].hasSshPassword]" + }, + "permissionScopes": { + "value": "[parameters('localUsers')[copyIndex()].permissionScopes]" + }, + "hasSharedKey": { + "value": "[tryGet(parameters('localUsers')[copyIndex()], 'hasSharedKey')]" + }, + "homeDirectory": { + "value": "[tryGet(parameters('localUsers')[copyIndex()], 'homeDirectory')]" + }, + "sshAuthorizedKeys": { + "value": "[tryGet(parameters('localUsers')[copyIndex()], 'sshAuthorizedKeys')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "14968464858285923305" + }, + "name": "Storage Account Local Users", + "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "sshAuthorizedKeysType": { + "type": "secureObject", + "properties": { + "secureList": { + "type": "array", + "items": { + "type": "object", + "properties": { + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Description used to store the function/usage of the key." + } + }, + "key": { + "type": "string", + "metadata": { + "description": "Required. SSH public key base64 encoded. The format should be: '{keyType} {keyData}', e.g. ssh-rsa AAAABBBB." + } + } + } + }, + "metadata": { + "description": "Optional. The list of SSH authorized keys." + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the local user used for SFTP Authentication." + } + }, + "hasSharedKey": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Indicates whether shared key exists. Set it to false to remove existing shared key." + } + }, + "hasSshKey": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether SSH key exists. Set it to false to remove existing SSH key." + } + }, + "hasSshPassword": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether SSH password exists. Set it to false to remove existing SSH password." + } + }, + "homeDirectory": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The local user home directory." + } + }, + "permissionScopes": { + "type": "array", + "metadata": { + "description": "Required. The permission scopes of the local user." + } + }, + "sshAuthorizedKeys": { + "$ref": "#/definitions/sshAuthorizedKeysType", + "metadata": { + "description": "Optional. The local user SSH authorized keys for SFTP." + } + } + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "localUsers": { + "type": "Microsoft.Storage/storageAccounts/localUsers", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]", + "properties": { + "hasSharedKey": "[parameters('hasSharedKey')]", + "hasSshKey": "[parameters('hasSshKey')]", + "hasSshPassword": "[parameters('hasSshPassword')]", + "homeDirectory": "[parameters('homeDirectory')]", + "permissionScopes": "[parameters('permissionScopes')]", + "sshAuthorizedKeys": "[tryGet(parameters('sshAuthorizedKeys'), 'secureList')]" + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed local user." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed local user." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed local user." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/localUsers', parameters('storageAccountName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_blobServices": { + "condition": "[not(empty(parameters('blobServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-BlobServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "containers": { + "value": "[tryGet(parameters('blobServices'), 'containers')]" + }, + "automaticSnapshotPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'automaticSnapshotPolicyEnabled')]" + }, + "changeFeedEnabled": { + "value": "[tryGet(parameters('blobServices'), 'changeFeedEnabled')]" + }, + "changeFeedRetentionInDays": { + "value": "[tryGet(parameters('blobServices'), 'changeFeedRetentionInDays')]" + }, + "containerDeleteRetentionPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyEnabled')]" + }, + "containerDeleteRetentionPolicyDays": { + "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyDays')]" + }, + "containerDeleteRetentionPolicyAllowPermanentDelete": { + "value": "[tryGet(parameters('blobServices'), 'containerDeleteRetentionPolicyAllowPermanentDelete')]" + }, + "corsRules": { + "value": "[tryGet(parameters('blobServices'), 'corsRules')]" + }, + "defaultServiceVersion": { + "value": "[tryGet(parameters('blobServices'), 'defaultServiceVersion')]" + }, + "deleteRetentionPolicyAllowPermanentDelete": { + "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyAllowPermanentDelete')]" + }, + "deleteRetentionPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyEnabled')]" + }, + "deleteRetentionPolicyDays": { + "value": "[tryGet(parameters('blobServices'), 'deleteRetentionPolicyDays')]" + }, + "isVersioningEnabled": { + "value": "[tryGet(parameters('blobServices'), 'isVersioningEnabled')]" + }, + "lastAccessTimeTrackingPolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'lastAccessTimeTrackingPolicyEnabled')]" + }, + "restorePolicyEnabled": { + "value": "[tryGet(parameters('blobServices'), 'restorePolicyEnabled')]" + }, + "restorePolicyDays": { + "value": "[tryGet(parameters('blobServices'), 'restorePolicyDays')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('blobServices'), 'diagnosticSettings')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "2306287879023715578" + }, + "name": "Storage Account blob Services", + "description": "This module deploys a Storage Account Blob Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "automaticSnapshotPolicyEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Automatic Snapshot is enabled if set to true." + } + }, + "changeFeedEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The blob service properties for change feed events. Indicates whether change feed event logging is enabled for the Blob service." + } + }, + "changeFeedRetentionInDays": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 146000, + "metadata": { + "description": "Optional. Indicates whether change feed event logging is enabled for the Blob service. Indicates the duration of changeFeed retention in days. If left blank, it indicates an infinite retention of the change feed." + } + }, + "containerDeleteRetentionPolicyEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The blob service properties for container soft delete. Indicates whether DeleteRetentionPolicy is enabled." + } + }, + "containerDeleteRetentionPolicyDays": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 365, + "metadata": { + "description": "Optional. Indicates the number of days that the deleted item should be retained." + } + }, + "containerDeleteRetentionPolicyAllowPermanentDelete": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. This property when set to true allows deletion of the soft deleted blob versions and snapshots. This property cannot be used with blob restore policy. This property only applies to blob service and does not apply to containers or file share." + } + }, + "corsRules": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Specifies CORS rules for the Blob service. You can include up to five CorsRule elements in the request. If no CorsRule elements are included in the request body, all CORS rules will be deleted, and CORS will be disabled for the Blob service." + } + }, + "defaultServiceVersion": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Indicates the default version to use for requests to the Blob service if an incoming request's version is not specified. Possible values include version 2008-10-27 and all more recent versions." + } + }, + "deleteRetentionPolicyEnabled": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. The blob service properties for blob soft delete." + } + }, + "deleteRetentionPolicyDays": { + "type": "int", + "defaultValue": 7, + "minValue": 1, + "maxValue": 365, + "metadata": { + "description": "Optional. Indicates the number of days that the deleted blob should be retained." + } + }, + "deleteRetentionPolicyAllowPermanentDelete": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. This property when set to true allows deletion of the soft deleted blob versions and snapshots. This property cannot be used with blob restore policy. This property only applies to blob service and does not apply to containers or file share." + } + }, + "isVersioningEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Use versioning to automatically maintain previous versions of your blobs." + } + }, + "lastAccessTimeTrackingPolicyEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The blob service property to configure last access time based tracking policy. When set to true last access time based tracking is enabled." + } + }, + "restorePolicyEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. The blob service properties for blob restore policy. If point-in-time restore is enabled, then versioning, change feed, and blob soft delete must also be enabled." + } + }, + "restorePolicyDays": { + "type": "int", + "defaultValue": 6, + "minValue": 1, + "metadata": { + "description": "Optional. How long this blob can be restored. It should be less than DeleteRetentionPolicy days." + } + }, + "containers": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Blob containers to create." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "name": "default" + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[parameters('storageAccountName')]" + }, + "blobServices": { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "properties": { + "automaticSnapshotPolicyEnabled": "[parameters('automaticSnapshotPolicyEnabled')]", + "changeFeed": "[if(parameters('changeFeedEnabled'), createObject('enabled', true(), 'retentionInDays', parameters('changeFeedRetentionInDays')), null())]", + "containerDeleteRetentionPolicy": { + "enabled": "[parameters('containerDeleteRetentionPolicyEnabled')]", + "days": "[parameters('containerDeleteRetentionPolicyDays')]", + "allowPermanentDelete": "[if(equals(parameters('containerDeleteRetentionPolicyEnabled'), true()), parameters('containerDeleteRetentionPolicyAllowPermanentDelete'), null())]" + }, + "cors": { + "corsRules": "[parameters('corsRules')]" + }, + "defaultServiceVersion": "[if(not(empty(parameters('defaultServiceVersion'))), parameters('defaultServiceVersion'), null())]", + "deleteRetentionPolicy": { + "enabled": "[parameters('deleteRetentionPolicyEnabled')]", + "days": "[parameters('deleteRetentionPolicyDays')]", + "allowPermanentDelete": "[if(and(parameters('deleteRetentionPolicyEnabled'), parameters('deleteRetentionPolicyAllowPermanentDelete')), true(), null())]" + }, + "isVersioningEnabled": "[parameters('isVersioningEnabled')]", + "lastAccessTimeTrackingPolicy": "[if(not(equals(reference('storageAccount', '2022-09-01', 'full').kind, 'Storage')), createObject('enable', parameters('lastAccessTimeTrackingPolicyEnabled'), 'name', if(equals(parameters('lastAccessTimeTrackingPolicyEnabled'), true()), 'AccessTimeTracking', null()), 'trackingGranularityInDays', if(equals(parameters('lastAccessTimeTrackingPolicyEnabled'), true()), 1, null())), null())]", + "restorePolicy": "[if(parameters('restorePolicyEnabled'), createObject('enabled', true(), 'days', parameters('restorePolicyDays')), null())]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "blobServices_diagnosticSettings": { + "copy": { + "name": "blobServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}', parameters('storageAccountName'), variables('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "blobServices" + ] + }, + "blobServices_container": { + "copy": { + "name": "blobServices_container", + "count": "[length(coalesce(parameters('containers'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Container-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "name": { + "value": "[coalesce(parameters('containers'), createArray())[copyIndex()].name]" + }, + "defaultEncryptionScope": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'defaultEncryptionScope')]" + }, + "denyEncryptionScopeOverride": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'denyEncryptionScopeOverride')]" + }, + "enableNfsV3AllSquash": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'enableNfsV3AllSquash')]" + }, + "enableNfsV3RootSquash": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'enableNfsV3RootSquash')]" + }, + "immutableStorageWithVersioningEnabled": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'immutableStorageWithVersioningEnabled')]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'metadata')]" + }, + "publicAccess": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'publicAccess')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "immutabilityPolicyProperties": { + "value": "[tryGet(coalesce(parameters('containers'), createArray())[copyIndex()], 'immutabilityPolicyProperties')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "7045309160947869799" + }, + "name": "Storage Account Blob Containers", + "description": "This module deploys a Storage Account Blob Container.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the storage container to deploy." + } + }, + "defaultEncryptionScope": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Default the container to use specified encryption scope for all writes." + } + }, + "denyEncryptionScopeOverride": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Block override of encryption scope from the container default." + } + }, + "enableNfsV3AllSquash": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable NFSv3 all squash on blob container." + } + }, + "enableNfsV3RootSquash": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Enable NFSv3 root squash on blob container." + } + }, + "immutableStorageWithVersioningEnabled": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. This is an immutable property, when set to true it enables object level immutability at the container level. The property is immutable and can only be set to true at the container creation time. Existing containers must undergo a migration process." + } + }, + "immutabilityPolicyName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. Name of the immutable policy." + } + }, + "immutabilityPolicyProperties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Configure immutability policy." + } + }, + "metadata": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. A name-value pair to associate with the container as metadata." + } + }, + "publicAccess": { + "type": "string", + "defaultValue": "None", + "allowedValues": [ + "Container", + "Blob", + "None" + ], + "metadata": { + "description": "Optional. Specifies whether data in the container may be accessed publicly and the level of access." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "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')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Blob Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]", + "Storage Blob Data Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b7e6dc6d-f1e8-4753-8033-0f276bb0955b')]", + "Storage Blob Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')]", + "Storage Blob Delegator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::blobServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[parameters('storageAccountName')]" + }, + "container": { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "properties": { + "defaultEncryptionScope": "[if(not(empty(parameters('defaultEncryptionScope'))), parameters('defaultEncryptionScope'), null())]", + "denyEncryptionScopeOverride": "[if(equals(parameters('denyEncryptionScopeOverride'), true()), parameters('denyEncryptionScopeOverride'), null())]", + "enableNfsV3AllSquash": "[if(equals(parameters('enableNfsV3AllSquash'), true()), parameters('enableNfsV3AllSquash'), null())]", + "enableNfsV3RootSquash": "[if(equals(parameters('enableNfsV3RootSquash'), true()), parameters('enableNfsV3RootSquash'), null())]", + "immutableStorageWithVersioning": "[if(equals(parameters('immutableStorageWithVersioningEnabled'), true()), createObject('enabled', parameters('immutableStorageWithVersioningEnabled')), null())]", + "metadata": "[parameters('metadata')]", + "publicAccess": "[parameters('publicAccess')]" + }, + "dependsOn": [ + "storageAccount::blobServices" + ] + }, + "container_roleAssignments": { + "copy": { + "name": "container_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}/containers/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "container" + ] + }, + "immutabilityPolicy": { + "condition": "[not(empty(coalesce(parameters('immutabilityPolicyProperties'), createObject())))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[parameters('immutabilityPolicyName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "containerName": { + "value": "[parameters('name')]" + }, + "immutabilityPeriodSinceCreationInDays": { + "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'immutabilityPeriodSinceCreationInDays')]" + }, + "allowProtectedAppendWrites": { + "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'allowProtectedAppendWrites')]" + }, + "allowProtectedAppendWritesAll": { + "value": "[tryGet(parameters('immutabilityPolicyProperties'), 'allowProtectedAppendWritesAll')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "2543276032744560941" + }, + "name": "Storage Account Blob Container Immutability Policies", + "description": "This module deploys a Storage Account Blob Container Immutability Policy.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "containerName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent container to apply the policy to. Required if the template is used in a standalone deployment." + } + }, + "immutabilityPeriodSinceCreationInDays": { + "type": "int", + "defaultValue": 365, + "metadata": { + "description": "Optional. The immutability period for the blobs in the container since the policy creation, in days." + } + }, + "allowProtectedAppendWrites": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to an append blob while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API." + } + }, + "allowProtectedAppendWritesAll": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to both \"Append and Block Blobs\" while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API. The \"allowProtectedAppendWrites\" and \"allowProtectedAppendWritesAll\" properties are mutually exclusive." + } + } + }, + "resources": [ + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies", + "apiVersion": "2022-09-01", + "name": "[format('{0}/{1}/{2}/{3}', parameters('storageAccountName'), 'default', parameters('containerName'), 'default')]", + "properties": { + "immutabilityPeriodSinceCreationInDays": "[parameters('immutabilityPeriodSinceCreationInDays')]", + "allowProtectedAppendWrites": "[parameters('allowProtectedAppendWrites')]", + "allowProtectedAppendWritesAll": "[parameters('allowProtectedAppendWritesAll')]" + } + } + ], + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed immutability policy." + }, + "value": "default" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed immutability policy." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies', parameters('storageAccountName'), 'default', parameters('containerName'), 'default')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed immutability policy." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "container", + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed container." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed container." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed container." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed blob service." + }, + "value": "[variables('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed blob service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices', parameters('storageAccountName'), variables('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the deployed blob service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_fileServices": { + "condition": "[not(empty(parameters('fileServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-FileServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('fileServices'), 'diagnosticSettings')]" + }, + "protocolSettings": { + "value": "[tryGet(parameters('fileServices'), 'protocolSettings')]" + }, + "shareDeleteRetentionPolicy": { + "value": "[tryGet(parameters('fileServices'), 'shareDeleteRetentionPolicy')]" + }, + "shares": { + "value": "[tryGet(parameters('fileServices'), 'shares')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "7463227074634701879" + }, + "name": "Storage Account File Share Services", + "description": "This module deploys a Storage Account File Share Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the file service." + } + }, + "protocolSettings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Protocol settings for file service." + } + }, + "shareDeleteRetentionPolicy": { + "type": "object", + "defaultValue": { + "enabled": true, + "days": 7 + }, + "metadata": { + "description": "Optional. The service properties for soft delete." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "shares": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. File shares to create." + } + } + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "fileServices": { + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('name'))]", + "properties": { + "protocolSettings": "[parameters('protocolSettings')]", + "shareDeleteRetentionPolicy": "[parameters('shareDeleteRetentionPolicy')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "fileServices_diagnosticSettings": { + "copy": { + "name": "fileServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/fileServices/{1}', parameters('storageAccountName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "fileServices" + ] + }, + "fileServices_shares": { + "copy": { + "name": "fileServices_shares", + "count": "[length(coalesce(parameters('shares'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-shares-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "fileServicesName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('shares'), createArray())[copyIndex()].name]" + }, + "accessTier": { + "value": "[coalesce(tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'accessTier'), if(equals(reference('storageAccount', '2023-04-01', 'full').kind, 'FileStorage'), 'Premium', 'TransactionOptimized'))]" + }, + "enabledProtocols": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'enabledProtocols')]" + }, + "rootSquash": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'rootSquash')]" + }, + "shareQuota": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'shareQuota')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('shares'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "1342480740201032357" + }, + "name": "Storage Account File Shares", + "description": "This module deploys a Storage Account File Share.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "fileServicesName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Conditional. The name of the parent file service. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the file share to create." + } + }, + "accessTier": { + "type": "string", + "defaultValue": "TransactionOptimized", + "allowedValues": [ + "Premium", + "Hot", + "Cool", + "TransactionOptimized" + ], + "metadata": { + "description": "Conditional. Access tier for specific share. Required if the Storage Account kind is set to FileStorage (should be set to \"Premium\"). GpV2 account can choose between TransactionOptimized (default), Hot, and Cool." + } + }, + "shareQuota": { + "type": "int", + "defaultValue": 5120, + "metadata": { + "description": "Optional. The maximum size of the share, in gigabytes. Must be greater than 0, and less than or equal to 5120 (5TB). For Large File Shares, the maximum size is 102400 (100TB)." + } + }, + "enabledProtocols": { + "type": "string", + "defaultValue": "SMB", + "allowedValues": [ + "NFS", + "SMB" + ], + "metadata": { + "description": "Optional. The authentication protocol that is used for the file share. Can only be specified when creating a share." + } + }, + "rootSquash": { + "type": "string", + "defaultValue": "NoRootSquash", + "allowedValues": [ + "AllSquash", + "NoRootSquash", + "RootSquash" + ], + "metadata": { + "description": "Optional. Permissions for NFS file shares are enforced by the client OS rather than the Azure Files service. Toggling the root squash behavior reduces the rights of the root user for NFS shares." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "resources": { + "storageAccount::fileService": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('fileServicesName'))]", + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "fileShare": { + "type": "Microsoft.Storage/storageAccounts/fileServices/shares", + "apiVersion": "2023-01-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]", + "properties": { + "accessTier": "[parameters('accessTier')]", + "shareQuota": "[parameters('shareQuota')]", + "rootSquash": "[if(equals(parameters('enabledProtocols'), 'NFS'), parameters('rootSquash'), null())]", + "enabledProtocols": "[parameters('enabledProtocols')]" + }, + "dependsOn": [ + "storageAccount::fileService" + ] + }, + "fileShare_roleAssignments": { + "condition": "[not(empty(parameters('roleAssignments')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Share-Rbac', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "fileShareResourceId": { + "value": "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]" + }, + "roleAssignments": { + "value": "[parameters('roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "8779226603522513073" + } + }, + "parameters": { + "roleAssignments": { + "type": "array", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "fileShareResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the file share to assign the roles to." + } + } + }, + "variables": { + "$fxv#0": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "scope": { + "type": "string", + "metadata": { + "description": "Required. The scope to deploy the role assignment to." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the role assignment." + } + }, + "roleDefinitionId": { + "type": "string", + "metadata": { + "description": "Required. The role definition Id to assign." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User", + "" + ], + "defaultValue": "", + "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", + "allowedValues": [ + "2.0" + ], + "defaultValue": "2.0", + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[[parameters('scope')]", + "name": "[[parameters('name')]", + "properties": { + "roleDefinitionId": "[[parameters('roleDefinitionId')]", + "principalId": "[[parameters('principalId')]", + "description": "[[parameters('description')]", + "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())]" + } + } + ] + }, + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "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')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage File Data SMB Share Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb')]", + "Storage File Data SMB Share Elevated Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a7264617-510b-434b-a828-9731dc254ea7')]", + "Storage File Data SMB Share Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'aba4ae5f-2193-4029-9191-0cb91df5e314')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": [ + { + "copy": { + "name": "fileShare_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('{0}-Share-Rbac-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "mode": "Incremental", + "expressionEvaluationOptions": { + "scope": "Outer" + }, + "template": "[variables('$fxv#0')]", + "parameters": { + "scope": { + "value": "[replace(parameters('fileShareResourceId'), '/shares/', '/fileShares/')]" + }, + "name": { + "value": "[guid(parameters('fileShareResourceId'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, 'tyfa')]" + }, + "roleDefinitionId": { + "value": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]" + }, + "principalId": { + "value": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]" + }, + "principalType": { + "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]" + }, + "description": { + "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]" + }, + "condition": { + "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]" + }, + "conditionVersion": { + "value": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]" + }, + "delegatedManagedIdentityResourceId": { + "value": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + } + } + } + } + ] + } + }, + "dependsOn": [ + "fileShare" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/fileServices/shares', parameters('storageAccountName'), parameters('fileServicesName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "fileServices", + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share service." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/fileServices', parameters('storageAccountName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_queueServices": { + "condition": "[not(empty(parameters('queueServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-QueueServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('queueServices'), 'diagnosticSettings')]" + }, + "queues": { + "value": "[tryGet(parameters('queueServices'), 'queues')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "10678250016540336570" + }, + "name": "Storage Account Queue Services", + "description": "This module deploys a Storage Account Queue Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "queues": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Queues to create." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "name": "default" + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "queueServices": { + "type": "Microsoft.Storage/storageAccounts/queueServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "properties": {}, + "dependsOn": [ + "storageAccount" + ] + }, + "queueServices_diagnosticSettings": { + "copy": { + "name": "queueServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/queueServices/{1}', parameters('storageAccountName'), variables('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "queueServices" + ] + }, + "queueServices_queues": { + "copy": { + "name": "queueServices_queues", + "count": "[length(coalesce(parameters('queues'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Queue-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "name": { + "value": "[coalesce(parameters('queues'), createArray())[copyIndex()].name]" + }, + "metadata": { + "value": "[tryGet(coalesce(parameters('queues'), createArray())[copyIndex()], 'metadata')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('queues'), createArray())[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "13487964166280180730" + }, + "name": "Storage Account Queues", + "description": "This module deploys a Storage Account Queue.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the storage queue to deploy." + } + }, + "metadata": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Required. A name-value pair that represents queue metadata." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "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')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Queue Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]", + "Storage Queue Data Message Processor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]", + "Storage Queue Data Message Sender": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a')]", + "Storage Queue Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '19e7f393-937e-4f77-808e-94535e297925')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::queueServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/queueServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "queue": { + "type": "Microsoft.Storage/storageAccounts/queueServices/queues", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "properties": { + "metadata": "[parameters('metadata')]" + }, + "dependsOn": [ + "storageAccount::queueServices" + ] + }, + "queue_roleAssignments": { + "copy": { + "name": "queue_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/queueServices/{1}/queues/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Storage/storageAccounts/queueServices/queues', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "queue" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed queue." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed queue." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/queueServices/queues', parameters('storageAccountName'), 'default', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed queue." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share service." + }, + "value": "[variables('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/queueServices', parameters('storageAccountName'), variables('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount_tableServices": { + "condition": "[not(empty(parameters('tableServices')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Storage-TableServices', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "storageAccountName": { + "value": "[parameters('name')]" + }, + "diagnosticSettings": { + "value": "[tryGet(parameters('tableServices'), 'diagnosticSettings')]" + }, + "tables": { + "value": "[tryGet(parameters('tableServices'), 'tables')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "16839054392438941735" + }, + "name": "Storage Account Table Services", + "description": "This module deploys a Storage Account Table Service.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "tables": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. tables to create." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + } + }, + "variables": { + "name": "default" + }, + "resources": { + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "tableServices": { + "type": "Microsoft.Storage/storageAccounts/tableServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), variables('name'))]", + "properties": {}, + "dependsOn": [ + "storageAccount" + ] + }, + "tableServices_diagnosticSettings": { + "copy": { + "name": "tableServices_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/tableServices/{1}', parameters('storageAccountName'), variables('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', variables('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "tableServices" + ] + }, + "tableServices_tables": { + "copy": { + "name": "tableServices_tables", + "count": "[length(parameters('tables'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Table-{1}', deployment().name, copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('tables')[copyIndex()].name]" + }, + "storageAccountName": { + "value": "[parameters('storageAccountName')]" + }, + "roleAssignments": { + "value": "[tryGet(parameters('tables')[copyIndex()], 'roleAssignments')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.28.1.47646", + "templateHash": "3177845984945141330" + }, + "name": "Storage Account Table", + "description": "This module deploys a Storage Account Table.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "storageAccountName": { + "type": "string", + "maxLength": 24, + "metadata": { + "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the table." + } + } + }, + "variables": { + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Reader and Data Access": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c12c1c16-33a1-487b-954d-41c89c60f349')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "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')]", + "Storage Account Key Operator Service Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '81a9662b-bebf-436f-a333-f67b29880f12')]", + "Storage Table Data Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')]", + "Storage Table Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '76199698-9eea-4c19-bc75-cec21354c6b6')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "storageAccount::tableServices": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts/tableServices", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "dependsOn": [ + "storageAccount" + ] + }, + "storageAccount": { + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-04-01", + "name": "[parameters('storageAccountName')]" + }, + "table": { + "type": "Microsoft.Storage/storageAccounts/tableServices/tables", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "dependsOn": [ + "storageAccount::tableServices" + ] + }, + "table_roleAssignments": { + "copy": { + "name": "table_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/tableServices/{1}/tables/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Storage/storageAccounts/tableServices/tables', parameters('storageAccountName'), 'default', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "table" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed file share service." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed file share service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/tableServices/tables', parameters('storageAccountName'), 'default', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed file share service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed table service." + }, + "value": "[variables('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed table service." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts/tableServices', parameters('storageAccountName'), variables('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed table service." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "storageAccount" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployed storage account." + }, + "value": "[resourceId('Microsoft.Storage/storageAccounts', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployed storage account." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group of the deployed storage account." + }, + "value": "[resourceGroup().name]" + }, + "primaryBlobEndpoint": { + "type": "string", + "metadata": { + "description": "The primary blob endpoint reference if blob services are deployed." + }, + "value": "[if(and(not(empty(parameters('blobServices'))), contains(parameters('blobServices'), 'containers')), reference(format('Microsoft.Storage/storageAccounts/{0}', parameters('name')), '2019-04-01').primaryEndpoints.blob, '')]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[coalesce(tryGet(tryGet(reference('storageAccount', '2022-09-01', 'full'), 'identity'), 'principalId'), '')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('storageAccount', '2022-09-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "dsMsi", + "rg", + "storageFileDataPrivilegedContributorRole", + "vnet" + ] + }, + "storageAccount_upload": { + "condition": "[or(or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only base')), equals(parameters('deploymentsToPerform'), 'Only assets & image'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-storage-upload-ds', deployment().name)]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('{0}-{1}', parameters('storageDeploymentScriptName'), variables('formattedTime'))]" + }, + "kind": { + "value": "AzurePowerShell" + }, + "azPowerShellVersion": { + "value": "12.0" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "managedIdentities": { + "value": { + "userAssignedResourcesIds": [ + "[resourceId(subscription().subscriptionId, parameters('resourceGroupName'), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('deploymentScriptManagedIdentityName'))]" + ] + } + }, + "scriptContent": { + "value": "[variables('$fxv#0')]" + }, + "environmentVariables": { + "value": "[map(coalesce(parameters('storageAccountFilesToUpload'), createArray()), lambda('file', createObject('name', format('__SCRIPT__{0}', replace(replace(lambdaVariables('file').name, '-', '__'), '.', '_')), 'value', tryGet(lambdaVariables('file'), 'value'), 'secureValue', tryGet(lambdaVariables('file'), 'secureValue'))))]" + }, + "arguments": { + "value": "[format(' -StorageAccountName \"{0}\" -TargetContainer \"{1}\"', parameters('assetsStorageAccountName'), parameters('assetsStorageAccountContainerName'))]" + }, + "timeout": { + "value": "PT30M" + }, + "cleanupPreference": { + "value": "Always" + }, + "location": { + "value": "[parameters('location')]" + }, + "storageAccountResourceId": { + "value": "[resourceId(subscription().subscriptionId, parameters('resourceGroupName'), 'Microsoft.Storage/storageAccounts', parameters('deploymentScriptStorageAccountName'))]" + }, + "subnetResourceIds": { + "value": [ + "[resourceId(subscription().subscriptionId, parameters('resourceGroupName'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('deploymentScriptSubnetName'))]" + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "10563518610969019714" + }, + "name": "Deployment Scripts", + "description": "This module deploys Deployment Scripts.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "managedIdentitiesType": { + "type": "object", + "properties": { + "userAssignedResourcesIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "environmentVariableType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the environment variable." + } + }, + "secureValue": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Required. The value of the secure environment variable." + } + }, + "value": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. The value of the environment variable." + } + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 90, + "metadata": { + "description": "Required. Name of the Deployment Script." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AzureCLI", + "AzurePowerShell" + ], + "metadata": { + "description": "Required. Specifies the Kind of the Deployment Script." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "azPowerShellVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Azure PowerShell module version to be used. See a list of supported Azure PowerShell versions: https://mcr.microsoft.com/v2/azuredeploymentscripts-powershell/tags/list." + } + }, + "azCliVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Azure CLI module version to be used. See a list of supported Azure CLI versions: https://mcr.microsoft.com/v2/azure-cli/tags/list." + } + }, + "scriptContent": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Script body. Max length: 32000 characters. To run an external script, use primaryScriptURI instead." + } + }, + "primaryScriptUri": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Uri for the external script. This is the entry point for the external script. To run an internal script, use the scriptContent parameter instead." + } + }, + "environmentVariables": { + "type": "array", + "items": { + "$ref": "#/definitions/environmentVariableType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The environment variables to pass over to the script." + } + }, + "supportingScriptUris": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. List of supporting files for the external script (defined in primaryScriptUri). Does not work with internal scripts (code defined in scriptContent)." + } + }, + "subnetResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of subnet IDs to use for the container group. This is required if you want to run the deployment script in a private network. When using a private network, the `Storage File Data Privileged Contributor` role needs to be assigned to the user-assigned managed identity and the deployment principal needs to have permissions to list the storage account keys. Also, Shared-Keys must not be disabled on the used storage account [ref](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/deployment-script-vnet)." + } + }, + "arguments": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Command-line arguments to pass to the script. Arguments are separated by spaces." + } + }, + "retentionInterval": { + "type": "string", + "defaultValue": "P1D", + "metadata": { + "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)." + } + }, + "baseTime": { + "type": "string", + "defaultValue": "[utcNow('yyyy-MM-dd-HH-mm-ss')]", + "metadata": { + "description": "Generated. Do not provide a value! This date value is used to make sure the script run every time the template is deployed." + } + }, + "runOnce": { + "type": "bool", + "defaultValue": false, + "metadata": { + "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." + } + }, + "cleanupPreference": { + "type": "string", + "defaultValue": "Always", + "allowedValues": [ + "Always", + "OnSuccess", + "OnExpiration" + ], + "metadata": { + "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)." + } + }, + "containerGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "storageAccountResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "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." + } + }, + "timeout": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + }, + { + "name": "subnetIds", + "count": "[length(coalesce(parameters('subnetResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('subnetResourceIds'), createArray())[copyIndex('subnetIds')]]" + } + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + }, + "containerSettings": { + "containerGroupName": "[parameters('containerGroupName')]", + "subnetIds": "[if(not(empty(coalesce(variables('subnetIds'), createArray()))), variables('subnetIds'), null())]" + }, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourcesIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourcesIds'), createObject()))), 'UserAssigned', null()), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]" + }, + "resources": { + "storageAccount": { + "condition": "[not(empty(parameters('storageAccountResourceId')))]", + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2021-04-01", + "subscriptionId": "[split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '////'), '/')[4]]", + "name": "[last(split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), 'dummyAccount'), '/'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.resources-deploymentscript.{0}.{1}', replace('0.3.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "deploymentScript": { + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2023-08-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "kind": "[parameters('kind')]", + "properties": { + "azPowerShellVersion": "[if(equals(parameters('kind'), 'AzurePowerShell'), parameters('azPowerShellVersion'), null())]", + "azCliVersion": "[if(equals(parameters('kind'), 'AzureCLI'), parameters('azCliVersion'), null())]", + "containerSettings": "[if(not(empty(variables('containerSettings'))), variables('containerSettings'), null())]", + "storageAccountSettings": "[if(not(empty(parameters('storageAccountResourceId'))), if(not(empty(parameters('storageAccountResourceId'))), createObject('storageAccountKey', if(empty(parameters('subnetResourceIds')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '//'), '/')[2], split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), 'dummyAccount'), '/'))), '2023-01-01').keys[0].value, null()), 'storageAccountName', last(split(parameters('storageAccountResourceId'), '/'))), null()), null())]", + "arguments": "[parameters('arguments')]", + "environmentVariables": "[parameters('environmentVariables')]", + "scriptContent": "[if(not(empty(parameters('scriptContent'))), parameters('scriptContent'), null())]", + "primaryScriptUri": "[if(not(empty(parameters('primaryScriptUri'))), parameters('primaryScriptUri'), null())]", + "supportingScriptUris": "[if(not(empty(parameters('supportingScriptUris'))), parameters('supportingScriptUris'), null())]", + "cleanupPreference": "[parameters('cleanupPreference')]", + "forceUpdateTag": "[if(parameters('runOnce'), resourceGroup().name, parameters('baseTime'))]", + "retentionInterval": "[parameters('retentionInterval')]", + "timeout": "[parameters('timeout')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "deploymentScript_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Resources/deploymentScripts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "deploymentScript" + ] + }, + "deploymentScript_roleAssignments": { + "copy": { + "name": "deploymentScript_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Resources/deploymentScripts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Resources/deploymentScripts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "deploymentScript" + ] + }, + "deploymentScriptLogs": { + "existing": true, + "type": "Microsoft.Resources/deploymentScripts/logs", + "apiVersion": "2023-08-01", + "name": "[format('{0}/{1}', parameters('name'), 'default')]", + "dependsOn": [ + "deploymentScript" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployment script." + }, + "value": "[resourceId('Microsoft.Resources/deploymentScripts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the deployment script was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployment script." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('deploymentScript', '2023-08-01', 'full').location]" + }, + "outputs": { + "type": "object", + "metadata": { + "description": "The output of the deployment script." + }, + "value": "[coalesce(tryGet(reference('deploymentScript'), 'outputs'), createObject())]" + }, + "deploymentScriptLogs": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The logs of the deployment script." + }, + "value": "[split(reference('deploymentScriptLogs').log, '\n')]" + } + } + } + }, + "dependsOn": [ + "assetsStorageAccount", + "dsMsi", + "dsStorageAccount", + "rg", + "vnet" + ] + }, + "imageTemplate": { + "condition": "[or(or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only assets & image')), equals(parameters('deploymentsToPerform'), 'Only image'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-it', deployment().name)]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "customizationSteps": { + "value": "[parameters('imageTemplateCustomizationSteps')]" + }, + "imageSource": { + "value": "[parameters('imageTemplateImageSource')]" + }, + "name": { + "value": "[parameters('imageTemplateName')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "managedIdentities": { + "value": { + "userAssignedResourceIds": [ + "[resourceId(subscription().subscriptionId, parameters('resourceGroupName'), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('imageManagedIdentityName'))]" + ] + } + }, + "distributions": { + "value": [ + { + "type": "SharedImage", + "sharedImageGalleryImageDefinitionResourceId": "[resourceId(subscription().subscriptionId, parameters('resourceGroupName'), 'Microsoft.Compute/galleries/images', parameters('computeGalleryName'), parameters('computeGalleryImageDefinitionName'))]" + } + ] + }, + "subnetResourceId": { + "value": "[resourceId(subscription().subscriptionId, parameters('resourceGroupName'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('imageSubnetName'))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "stagingResourceGroupResourceId": { + "value": "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('imageTemplateResourceGroupName'))]" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Contributor", + "principalId": "[if(or(equals(parameters('deploymentsToPerform'), 'Only assets & image'), equals(parameters('deploymentsToPerform'), 'Only image')), reference('dsMsi_existing').principalId, reference('dsMsi').outputs.principalId.value)]", + "principalType": "ServicePrincipal" + } + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "3107459634056863407" + }, + "name": "Virtual Machine Image Templates", + "description": "This module deploys a Virtual Machine Image Template that can be consumed by Azure Image Builder (AIB).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "managedIdentitiesType": { + "type": "object", + "properties": { + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource. Required if a user assigned identity is used for encryption." + } + } + } + }, + "distributionType": { + "type": "object", + "discriminator": { + "propertyName": "type", + "mapping": { + "SharedImage": { + "$ref": "#/definitions/sharedImageDistributionType" + }, + "ManagedImage": { + "$ref": "#/definitions/managedImageDistributionType" + }, + "VHD": { + "$ref": "#/definitions/unManagedDistributionType" + } + } + } + }, + "sharedImageDistributionType": { + "type": "object", + "properties": { + "runOutputName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name to be used for the associated RunOutput. If not provided, a name will be calculated." + } + }, + "artifactTags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags that will be applied to the artifact once it has been created/updated by the distributor. If not provided will set tags based on the provided image source." + } + }, + "type": { + "type": "string", + "allowedValues": [ + "SharedImage" + ], + "metadata": { + "description": "Required. The type of distribution." + } + }, + "sharedImageGalleryImageDefinitionResourceId": { + "type": "string", + "metadata": { + "description": "Conditional. Resource ID of Compute Gallery Image Definition to distribute image to, e.g.: /subscriptions//resourceGroups//providers/Microsoft.Compute/galleries//images/." + } + }, + "sharedImageGalleryImageDefinitionTargetVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Version of the Compute Gallery Image. Supports the following Version Syntax: Major.Minor.Build (i.e., '1.1.1' or '10.1.2'). If not provided, a version will be calculated." + } + }, + "excludeFromLatest": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. The exclude from latest flag of the image. Defaults to [false]." + } + }, + "replicationRegions": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The replication regions of the image. Defaults to the value of the 'location' parameter." + } + }, + "storageAccountType": { + "type": "string", + "allowedValues": [ + "Standard_LRS", + "Standard_ZRS" + ], + "nullable": true, + "metadata": { + "description": "Optional. The storage account type of the image. Defaults to [Standard_LRS]." + } + } + } + }, + "unManagedDistributionType": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "VHD" + ], + "metadata": { + "description": "Required. The type of distribution." + } + }, + "runOutputName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name to be used for the associated RunOutput. If not provided, a name will be calculated." + } + }, + "artifactTags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags that will be applied to the artifact once it has been created/updated by the distributor. If not provided will set tags based on the provided image source." + } + }, + "imageName": { + "type": "string", + "metadata": { + "description": "Conditional. Name of the managed or unmanaged image that will be created." + } + } + } + }, + "managedImageDistributionType": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "ManagedImage" + ], + "metadata": { + "description": "Required. The type of distribution." + } + }, + "runOutputName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name to be used for the associated RunOutput. If not provided, a name will be calculated." + } + }, + "artifactTags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags that will be applied to the artifact once it has been created/updated by the distributor. If not provided will set tags based on the provided image source." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Azure location for the image, should match if image already exists. Defaults to the value of the 'location' parameter." + } + }, + "imageResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. The resource ID of the managed image. Defaults to a compute image with name 'imageName-baseTime' in the current resource group." + } + }, + "imageName": { + "type": "string", + "metadata": { + "description": "Conditional. Name of the managed or unmanaged image that will be created." + } + } + } + }, + "validationProcessType": { + "type": "object", + "properties": { + "continueDistributeOnFailure": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If validation fails and this field is set to false, output image(s) will not be distributed. This is the default behavior. If validation fails and this field is set to true, output image(s) will still be distributed. Please use this option with caution as it may result in bad images being distributed for use. In either case (true or false), the end to end image run will be reported as having failed in case of a validation failure. [Note: This field has no effect if validation succeeds.]." + } + }, + "inVMValidations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "allowedValues": [ + "File", + "PowerShell", + "Shell" + ], + "metadata": { + "description": "Required. The type of validation." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Friendly Name to provide context on what this validation step does." + } + }, + "scriptUri": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. URI of the PowerShell script to be run for validation. It can be a github link, Azure Storage URI, etc." + } + }, + "inline": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Array of commands to be run, separated by commas." + } + }, + "validExitCodes": { + "type": "array", + "items": { + "type": "int" + }, + "nullable": true, + "metadata": { + "description": "Optional. Valid codes that can be returned from the script/inline command, this avoids reported failure of the script/inline command." + } + }, + "sha256Checksum": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Value of sha256 checksum of the file, you generate this locally, and then Image Builder will checksum and validate." + } + }, + "sourceUri": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The source URI of the file." + } + }, + "destination": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Destination of the file." + } + }, + "runAsSystem": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If specified, the PowerShell script will be run with elevated privileges using the Local System user. Can only be true when the runElevated field above is set to true." + } + }, + "runElevated": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If specified, the PowerShell script will be run with elevated privileges." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of validators that will be performed on the image. Azure Image Builder supports File, PowerShell and Shell validators." + } + }, + "sourceValidationOnly": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If this field is set to true, the image specified in the 'source' section will directly be validated. No separate build will be run to generate and then validate a customized image. Not supported when performing customizations, validations or distributions on the image." + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name prefix of the Image Template to be built by the Azure Image Builder service." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "buildTimeoutInMinutes": { + "type": "int", + "defaultValue": 0, + "minValue": 0, + "maxValue": 960, + "metadata": { + "description": "Optional. The image build timeout in minutes. 0 means the default 240 minutes." + } + }, + "vmSize": { + "type": "string", + "defaultValue": "Standard_D2s_v3", + "metadata": { + "description": "Optional. Specifies the size for the VM." + } + }, + "osDiskSizeGB": { + "type": "int", + "defaultValue": 128, + "metadata": { + "description": "Optional. Specifies the size of OS disk." + } + }, + "subnetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of an already existing subnet, e.g.: /subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks//subnets/.

If no value is provided, a new temporary VNET and subnet will be created in the staging resource group and will be deleted along with the remaining temporary resources." + } + }, + "imageSource": { + "type": "object", + "metadata": { + "description": "Required. Image source definition in object format." + } + }, + "customizationSteps": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Customization steps to be run when building the VM image." + } + }, + "stagingResourceGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the staging resource group in the same subscription and location as the image template that will be used to build the image.

If this field is empty, a resource group with a random name will be created.

If the resource group specified in this field doesn't exist, it will be created with the same name.

If the resource group specified exists, it must be empty and in the same region as the image template.

The resource group created will be deleted during template deletion if this field is empty or the resource group specified doesn't exist,

but if the resource group specified exists the resources created in the resource group will be deleted during template deletion and the resource group itself will remain." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "baseTime": { + "type": "string", + "defaultValue": "[utcNow('yyyy-MM-dd-HH-mm-ss')]", + "metadata": { + "description": "Generated. Do not provide a value! This date value is used to generate a unique image template name." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "distributions": { + "type": "array", + "items": { + "$ref": "#/definitions/distributionType" + }, + "metadata": { + "description": "Required. The distribution targets where the image output needs to go to." + } + }, + "vmUserAssignedIdentities": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts. Be aware, the user assigned identities specified in the 'managedIdentities' parameter must have the 'Managed Identity Operator' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Required. The managed identity definition for this resource." + } + }, + "validationProcess": { + "$ref": "#/definitions/validationProcessType", + "metadata": { + "description": "Optional. Configuration options and list of validations to be performed on the resulting image." + } + }, + "optimizeVmBoot": { + "type": "string", + "nullable": true, + "allowedValues": [ + "Enabled", + "Disabled" + ], + "metadata": { + "description": "Optional. The optimize property can be enabled while creating a VM image and allows VM optimization to improve image creation time." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]" + }, + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.virtualmachineimages-imagetemplate.{0}.{1}', replace('0.3.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "imageTemplate": { + "type": "Microsoft.VirtualMachineImages/imageTemplates", + "apiVersion": "2023-07-01", + "name": "[format('{0}-{1}', parameters('name'), parameters('baseTime'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "properties": { + "copy": [ + { + "name": "distribute", + "count": "[length(parameters('distributions'))]", + "input": "[union(createObject('type', parameters('distributions')[copyIndex('distribute')].type, 'artifactTags', coalesce(tryGet(parameters('distributions')[copyIndex('distribute')], 'artifactTags'), createObject('sourceType', parameters('imageSource').type, 'sourcePublisher', tryGet(parameters('imageSource'), 'publisher'), 'sourceOffer', tryGet(parameters('imageSource'), 'offer'), 'sourceSku', tryGet(parameters('imageSource'), 'sku'), 'sourceVersion', tryGet(parameters('imageSource'), 'version'), 'sourceImageId', tryGet(parameters('imageSource'), 'imageId'), 'sourceImageVersionID', tryGet(parameters('imageSource'), 'imageVersionID'), 'creationTime', parameters('baseTime')))), if(equals(parameters('distributions')[copyIndex('distribute')].type, 'ManagedImage'), createObject('runOutputName', coalesce(tryGet(parameters('distributions')[copyIndex('distribute')], 'runOutputName'), format('{0}-{1}-ManagedImage', parameters('distributions')[copyIndex('distribute')].imageName, parameters('baseTime'))), 'location', coalesce(tryGet(parameters('distributions')[copyIndex('distribute')], 'location'), parameters('location')), 'imageId', coalesce(tryGet(parameters('distributions')[copyIndex('distribute')], 'imageResourceId'), format('{0}/resourceGroups/{1}/providers/Microsoft.Compute/images/{2}-{3}', subscription().id, resourceGroup().name, parameters('distributions')[copyIndex('distribute')].imageName, parameters('baseTime')))), createObject()), if(equals(parameters('distributions')[copyIndex('distribute')].type, 'SharedImage'), createObject('runOutputName', coalesce(tryGet(parameters('distributions')[copyIndex('distribute')], 'runOutputName'), if(not(empty(tryGet(parameters('distributions')[copyIndex('distribute')], 'sharedImageGalleryImageDefinitionResourceId'))), format('{0}-SharedImage', last(split(coalesce(parameters('distributions')[copyIndex('distribute')].sharedImageGalleryImageDefinitionResourceId, '/'), '/'))), 'SharedImage')), 'galleryImageId', if(not(empty(tryGet(parameters('distributions')[copyIndex('distribute')], 'sharedImageGalleryImageDefinitionTargetVersion'))), format('{0}/versions/{1}', parameters('distributions')[copyIndex('distribute')].sharedImageGalleryImageDefinitionResourceId, parameters('distributions')[copyIndex('distribute')].sharedImageGalleryImageDefinitionTargetVersion), parameters('distributions')[copyIndex('distribute')].sharedImageGalleryImageDefinitionResourceId), 'excludeFromLatest', coalesce(tryGet(parameters('distributions')[copyIndex('distribute')], 'excludeFromLatest'), false()), 'replicationRegions', coalesce(tryGet(parameters('distributions')[copyIndex('distribute')], 'replicationRegions'), createArray(parameters('location'))), 'storageAccountType', coalesce(tryGet(parameters('distributions')[copyIndex('distribute')], 'storageAccountType'), 'Standard_LRS')), createObject()), if(equals(parameters('distributions')[copyIndex('distribute')].type, 'VHD'), createObject('runOutputName', coalesce(tryGet(parameters('distributions')[copyIndex('distribute')], 'runOutputName'), format('{0}-VHD', parameters('distributions')[copyIndex('distribute')].imageName))), createObject()))]" + } + ], + "buildTimeoutInMinutes": "[parameters('buildTimeoutInMinutes')]", + "vmProfile": { + "vmSize": "[parameters('vmSize')]", + "osDiskSizeGB": "[parameters('osDiskSizeGB')]", + "userAssignedIdentities": "[parameters('vmUserAssignedIdentities')]", + "vnetConfig": "[if(not(empty(parameters('subnetResourceId'))), createObject('subnetId', parameters('subnetResourceId')), null())]" + }, + "source": "[parameters('imageSource')]", + "customize": "[parameters('customizationSteps')]", + "stagingResourceGroup": "[parameters('stagingResourceGroupResourceId')]", + "validate": "[parameters('validationProcess')]", + "optimize": "[if(not(equals(parameters('optimizeVmBoot'), null())), createObject('vmBoot', createObject('state', parameters('optimizeVmBoot'))), null())]" + } + }, + "imageTemplate_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.VirtualMachineImages/imageTemplates/{0}', format('{0}-{1}', parameters('name'), parameters('baseTime')))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "imageTemplate" + ] + }, + "imageTemplate_roleAssignments": { + "copy": { + "name": "imageTemplate_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.VirtualMachineImages/imageTemplates/{0}', format('{0}-{1}', parameters('name'), parameters('baseTime')))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.VirtualMachineImages/imageTemplates', format('{0}-{1}', parameters('name'), parameters('baseTime'))), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "imageTemplate" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the image template." + }, + "value": "[resourceId('Microsoft.VirtualMachineImages/imageTemplates', format('{0}-{1}', parameters('name'), parameters('baseTime')))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the image template was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The full name of the deployed image template." + }, + "value": "[format('{0}-{1}', parameters('name'), parameters('baseTime'))]" + }, + "namePrefix": { + "type": "string", + "metadata": { + "description": "The prefix of the image template name provided as input." + }, + "value": "[parameters('name')]" + }, + "runThisCommand": { + "type": "string", + "metadata": { + "description": "The command to run in order to trigger the image build." + }, + "value": "[format('Invoke-AzResourceAction -ResourceName {0} -ResourceGroupName {1} -ResourceType Microsoft.VirtualMachineImages/imageTemplates -Action Run -Force', format('{0}-{1}', parameters('name'), parameters('baseTime')), resourceGroup().name)]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('imageTemplate', '2023-07-01', 'full').location]" + } + } + } + }, + "dependsOn": [ + "azureComputeGallery", + "dsMsi", + "dsMsi_existing", + "imageMSI", + "imageMSI_rbac", + "imageTemplateRg", + "rg", + "storageAccount_upload", + "vnet" + ] + }, + "imageTemplate_trigger": { + "condition": "[or(or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only assets & image')), equals(parameters('deploymentsToPerform'), 'Only image'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-imageTemplate-trigger-ds', deployment().name)]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('{0}-{1}-{2}', parameters('imageTemplateDeploymentScriptName'), variables('formattedTime'), if(or(or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only assets & image')), equals(parameters('deploymentsToPerform'), 'Only image')), reference('imageTemplate').outputs.name.value, ''))]" + }, + "kind": { + "value": "AzurePowerShell" + }, + "azPowerShellVersion": { + "value": "12.0" + }, + "managedIdentities": { + "value": { + "userAssignedResourcesIds": [ + "[resourceId(subscription().subscriptionId, parameters('resourceGroupName'), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('deploymentScriptManagedIdentityName'))]" + ] + } + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "scriptContent": "[if(or(or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only assets & image')), equals(parameters('deploymentsToPerform'), 'Only image')), createObject('value', reference('imageTemplate').outputs.runThisCommand.value), createObject('value', ''))]", + "timeout": { + "value": "PT30M" + }, + "cleanupPreference": { + "value": "Always" + }, + "location": { + "value": "[parameters('location')]" + }, + "storageAccountResourceId": { + "value": "[resourceId(subscription().subscriptionId, parameters('resourceGroupName'), 'Microsoft.Storage/storageAccounts', parameters('deploymentScriptStorageAccountName'))]" + }, + "subnetResourceIds": { + "value": [ + "[resourceId(subscription().subscriptionId, parameters('resourceGroupName'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('deploymentScriptSubnetName'))]" + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "10563518610969019714" + }, + "name": "Deployment Scripts", + "description": "This module deploys Deployment Scripts.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "managedIdentitiesType": { + "type": "object", + "properties": { + "userAssignedResourcesIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "environmentVariableType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the environment variable." + } + }, + "secureValue": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Required. The value of the secure environment variable." + } + }, + "value": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. The value of the environment variable." + } + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 90, + "metadata": { + "description": "Required. Name of the Deployment Script." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AzureCLI", + "AzurePowerShell" + ], + "metadata": { + "description": "Required. Specifies the Kind of the Deployment Script." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "azPowerShellVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Azure PowerShell module version to be used. See a list of supported Azure PowerShell versions: https://mcr.microsoft.com/v2/azuredeploymentscripts-powershell/tags/list." + } + }, + "azCliVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Azure CLI module version to be used. See a list of supported Azure CLI versions: https://mcr.microsoft.com/v2/azure-cli/tags/list." + } + }, + "scriptContent": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Script body. Max length: 32000 characters. To run an external script, use primaryScriptURI instead." + } + }, + "primaryScriptUri": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Uri for the external script. This is the entry point for the external script. To run an internal script, use the scriptContent parameter instead." + } + }, + "environmentVariables": { + "type": "array", + "items": { + "$ref": "#/definitions/environmentVariableType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The environment variables to pass over to the script." + } + }, + "supportingScriptUris": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. List of supporting files for the external script (defined in primaryScriptUri). Does not work with internal scripts (code defined in scriptContent)." + } + }, + "subnetResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of subnet IDs to use for the container group. This is required if you want to run the deployment script in a private network. When using a private network, the `Storage File Data Privileged Contributor` role needs to be assigned to the user-assigned managed identity and the deployment principal needs to have permissions to list the storage account keys. Also, Shared-Keys must not be disabled on the used storage account [ref](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/deployment-script-vnet)." + } + }, + "arguments": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Command-line arguments to pass to the script. Arguments are separated by spaces." + } + }, + "retentionInterval": { + "type": "string", + "defaultValue": "P1D", + "metadata": { + "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)." + } + }, + "baseTime": { + "type": "string", + "defaultValue": "[utcNow('yyyy-MM-dd-HH-mm-ss')]", + "metadata": { + "description": "Generated. Do not provide a value! This date value is used to make sure the script run every time the template is deployed." + } + }, + "runOnce": { + "type": "bool", + "defaultValue": false, + "metadata": { + "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." + } + }, + "cleanupPreference": { + "type": "string", + "defaultValue": "Always", + "allowedValues": [ + "Always", + "OnSuccess", + "OnExpiration" + ], + "metadata": { + "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)." + } + }, + "containerGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "storageAccountResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "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." + } + }, + "timeout": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + }, + { + "name": "subnetIds", + "count": "[length(coalesce(parameters('subnetResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('subnetResourceIds'), createArray())[copyIndex('subnetIds')]]" + } + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + }, + "containerSettings": { + "containerGroupName": "[parameters('containerGroupName')]", + "subnetIds": "[if(not(empty(coalesce(variables('subnetIds'), createArray()))), variables('subnetIds'), null())]" + }, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourcesIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourcesIds'), createObject()))), 'UserAssigned', null()), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]" + }, + "resources": { + "storageAccount": { + "condition": "[not(empty(parameters('storageAccountResourceId')))]", + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2021-04-01", + "subscriptionId": "[split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '////'), '/')[4]]", + "name": "[last(split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), 'dummyAccount'), '/'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.resources-deploymentscript.{0}.{1}', replace('0.3.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "deploymentScript": { + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2023-08-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "kind": "[parameters('kind')]", + "properties": { + "azPowerShellVersion": "[if(equals(parameters('kind'), 'AzurePowerShell'), parameters('azPowerShellVersion'), null())]", + "azCliVersion": "[if(equals(parameters('kind'), 'AzureCLI'), parameters('azCliVersion'), null())]", + "containerSettings": "[if(not(empty(variables('containerSettings'))), variables('containerSettings'), null())]", + "storageAccountSettings": "[if(not(empty(parameters('storageAccountResourceId'))), if(not(empty(parameters('storageAccountResourceId'))), createObject('storageAccountKey', if(empty(parameters('subnetResourceIds')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '//'), '/')[2], split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), 'dummyAccount'), '/'))), '2023-01-01').keys[0].value, null()), 'storageAccountName', last(split(parameters('storageAccountResourceId'), '/'))), null()), null())]", + "arguments": "[parameters('arguments')]", + "environmentVariables": "[parameters('environmentVariables')]", + "scriptContent": "[if(not(empty(parameters('scriptContent'))), parameters('scriptContent'), null())]", + "primaryScriptUri": "[if(not(empty(parameters('primaryScriptUri'))), parameters('primaryScriptUri'), null())]", + "supportingScriptUris": "[if(not(empty(parameters('supportingScriptUris'))), parameters('supportingScriptUris'), null())]", + "cleanupPreference": "[parameters('cleanupPreference')]", + "forceUpdateTag": "[if(parameters('runOnce'), resourceGroup().name, parameters('baseTime'))]", + "retentionInterval": "[parameters('retentionInterval')]", + "timeout": "[parameters('timeout')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "deploymentScript_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Resources/deploymentScripts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "deploymentScript" + ] + }, + "deploymentScript_roleAssignments": { + "copy": { + "name": "deploymentScript_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Resources/deploymentScripts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Resources/deploymentScripts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "deploymentScript" + ] + }, + "deploymentScriptLogs": { + "existing": true, + "type": "Microsoft.Resources/deploymentScripts/logs", + "apiVersion": "2023-08-01", + "name": "[format('{0}/{1}', parameters('name'), 'default')]", + "dependsOn": [ + "deploymentScript" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployment script." + }, + "value": "[resourceId('Microsoft.Resources/deploymentScripts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the deployment script was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployment script." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('deploymentScript', '2023-08-01', 'full').location]" + }, + "outputs": { + "type": "object", + "metadata": { + "description": "The output of the deployment script." + }, + "value": "[coalesce(tryGet(reference('deploymentScript'), 'outputs'), createObject())]" + }, + "deploymentScriptLogs": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The logs of the deployment script." + }, + "value": "[split(reference('deploymentScriptLogs').log, '\n')]" + } + } + } + }, + "dependsOn": [ + "dsMsi", + "dsStorageAccount", + "imageTemplate", + "rg", + "storageAccount_upload", + "vnet" + ] + }, + "imageTemplate_wait": { + "condition": "[and(parameters('waitForImageBuild'), or(or(equals(parameters('deploymentsToPerform'), 'All'), equals(parameters('deploymentsToPerform'), 'Only assets & image')), equals(parameters('deploymentsToPerform'), 'Only image')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-imageTemplate-wait-ds', deployment().name)]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[format('{0}-{1}', parameters('waitDeploymentScriptName'), variables('formattedTime'))]" + }, + "kind": { + "value": "AzurePowerShell" + }, + "azPowerShellVersion": { + "value": "12.0" + }, + "managedIdentities": { + "value": { + "userAssignedResourcesIds": [ + "[resourceId(subscription().subscriptionId, parameters('resourceGroupName'), 'Microsoft.ManagedIdentity/userAssignedIdentities', parameters('deploymentScriptManagedIdentityName'))]" + ] + } + }, + "scriptContent": { + "value": "[variables('$fxv#1')]" + }, + "arguments": { + "value": "[format(' -ImageTemplateName \"{0}\" -ResourceGroupName \"{1}\"', reference('imageTemplate').outputs.name.value, parameters('resourceGroupName'))]" + }, + "timeout": { + "value": "[parameters('waitForImageBuildTimeout')]" + }, + "cleanupPreference": { + "value": "Always" + }, + "location": { + "value": "[parameters('location')]" + }, + "storageAccountResourceId": { + "value": "[resourceId(subscription().subscriptionId, parameters('resourceGroupName'), 'Microsoft.Storage/storageAccounts', parameters('deploymentScriptStorageAccountName'))]" + }, + "subnetResourceIds": { + "value": [ + "[resourceId(subscription().subscriptionId, parameters('resourceGroupName'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('deploymentScriptSubnetName'))]" + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "10563518610969019714" + }, + "name": "Deployment Scripts", + "description": "This module deploys Deployment Scripts.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "managedIdentitiesType": { + "type": "object", + "properties": { + "userAssignedResourcesIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "environmentVariableType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the environment variable." + } + }, + "secureValue": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Required. The value of the secure environment variable." + } + }, + "value": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. The value of the environment variable." + } + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "maxLength": 90, + "metadata": { + "description": "Required. Name of the Deployment Script." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "AzureCLI", + "AzurePowerShell" + ], + "metadata": { + "description": "Required. Specifies the Kind of the Deployment Script." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "description": "Optional. The managed identity definition for this resource." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "azPowerShellVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Azure PowerShell module version to be used. See a list of supported Azure PowerShell versions: https://mcr.microsoft.com/v2/azuredeploymentscripts-powershell/tags/list." + } + }, + "azCliVersion": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Azure CLI module version to be used. See a list of supported Azure CLI versions: https://mcr.microsoft.com/v2/azure-cli/tags/list." + } + }, + "scriptContent": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Script body. Max length: 32000 characters. To run an external script, use primaryScriptURI instead." + } + }, + "primaryScriptUri": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Uri for the external script. This is the entry point for the external script. To run an internal script, use the scriptContent parameter instead." + } + }, + "environmentVariables": { + "type": "array", + "items": { + "$ref": "#/definitions/environmentVariableType" + }, + "nullable": true, + "metadata": { + "description": "Optional. The environment variables to pass over to the script." + } + }, + "supportingScriptUris": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. List of supporting files for the external script (defined in primaryScriptUri). Does not work with internal scripts (code defined in scriptContent)." + } + }, + "subnetResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of subnet IDs to use for the container group. This is required if you want to run the deployment script in a private network. When using a private network, the `Storage File Data Privileged Contributor` role needs to be assigned to the user-assigned managed identity and the deployment principal needs to have permissions to list the storage account keys. Also, Shared-Keys must not be disabled on the used storage account [ref](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/deployment-script-vnet)." + } + }, + "arguments": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Command-line arguments to pass to the script. Arguments are separated by spaces." + } + }, + "retentionInterval": { + "type": "string", + "defaultValue": "P1D", + "metadata": { + "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)." + } + }, + "baseTime": { + "type": "string", + "defaultValue": "[utcNow('yyyy-MM-dd-HH-mm-ss')]", + "metadata": { + "description": "Generated. Do not provide a value! This date value is used to make sure the script run every time the template is deployed." + } + }, + "runOnce": { + "type": "bool", + "defaultValue": false, + "metadata": { + "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." + } + }, + "cleanupPreference": { + "type": "string", + "defaultValue": "Always", + "allowedValues": [ + "Always", + "OnSuccess", + "OnExpiration" + ], + "metadata": { + "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)." + } + }, + "containerGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "storageAccountResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "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." + } + }, + "timeout": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + }, + { + "name": "subnetIds", + "count": "[length(coalesce(parameters('subnetResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('subnetResourceIds'), createArray())[copyIndex('subnetIds')]]" + } + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + }, + "containerSettings": { + "containerGroupName": "[parameters('containerGroupName')]", + "subnetIds": "[if(not(empty(coalesce(variables('subnetIds'), createArray()))), variables('subnetIds'), null())]" + }, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourcesIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourcesIds'), createObject()))), 'UserAssigned', null()), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]" + }, + "resources": { + "storageAccount": { + "condition": "[not(empty(parameters('storageAccountResourceId')))]", + "existing": true, + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2021-04-01", + "subscriptionId": "[split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '////'), '/')[4]]", + "name": "[last(split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), 'dummyAccount'), '/'))]" + }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.resources-deploymentscript.{0}.{1}', replace('0.3.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "deploymentScript": { + "type": "Microsoft.Resources/deploymentScripts", + "apiVersion": "2023-08-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "kind": "[parameters('kind')]", + "properties": { + "azPowerShellVersion": "[if(equals(parameters('kind'), 'AzurePowerShell'), parameters('azPowerShellVersion'), null())]", + "azCliVersion": "[if(equals(parameters('kind'), 'AzureCLI'), parameters('azCliVersion'), null())]", + "containerSettings": "[if(not(empty(variables('containerSettings'))), variables('containerSettings'), null())]", + "storageAccountSettings": "[if(not(empty(parameters('storageAccountResourceId'))), if(not(empty(parameters('storageAccountResourceId'))), createObject('storageAccountKey', if(empty(parameters('subnetResourceIds')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '//'), '/')[2], split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(if(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountResourceId'), 'dummyAccount'), '/'))), '2023-01-01').keys[0].value, null()), 'storageAccountName', last(split(parameters('storageAccountResourceId'), '/'))), null()), null())]", + "arguments": "[parameters('arguments')]", + "environmentVariables": "[parameters('environmentVariables')]", + "scriptContent": "[if(not(empty(parameters('scriptContent'))), parameters('scriptContent'), null())]", + "primaryScriptUri": "[if(not(empty(parameters('primaryScriptUri'))), parameters('primaryScriptUri'), null())]", + "supportingScriptUris": "[if(not(empty(parameters('supportingScriptUris'))), parameters('supportingScriptUris'), null())]", + "cleanupPreference": "[parameters('cleanupPreference')]", + "forceUpdateTag": "[if(parameters('runOnce'), resourceGroup().name, parameters('baseTime'))]", + "retentionInterval": "[parameters('retentionInterval')]", + "timeout": "[parameters('timeout')]" + }, + "dependsOn": [ + "storageAccount" + ] + }, + "deploymentScript_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Resources/deploymentScripts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "deploymentScript" + ] + }, + "deploymentScript_roleAssignments": { + "copy": { + "name": "deploymentScript_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Resources/deploymentScripts/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Resources/deploymentScripts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "deploymentScript" + ] + }, + "deploymentScriptLogs": { + "existing": true, + "type": "Microsoft.Resources/deploymentScripts/logs", + "apiVersion": "2023-08-01", + "name": "[format('{0}/{1}', parameters('name'), 'default')]", + "dependsOn": [ + "deploymentScript" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the deployment script." + }, + "value": "[resourceId('Microsoft.Resources/deploymentScripts', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the deployment script was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the deployment script." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('deploymentScript', '2023-08-01', 'full').location]" + }, + "outputs": { + "type": "object", + "metadata": { + "description": "The output of the deployment script." + }, + "value": "[coalesce(tryGet(reference('deploymentScript'), 'outputs'), createObject())]" + }, + "deploymentScriptLogs": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The logs of the deployment script." + }, + "value": "[split(reference('deploymentScriptLogs').log, '\n')]" + } + } + } + }, + "dependsOn": [ + "dsMsi", + "dsStorageAccount", + "imageTemplate", + "imageTemplate_trigger", + "rg", + "vnet" + ] + } + } +} \ No newline at end of file diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/AzureComputeGalleries.svg b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/AzureComputeGalleries.svg new file mode 100644 index 0000000000..f6b1879361 --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/AzureComputeGalleries.svg @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Deployment-Script.png b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Deployment-Script.png new file mode 100644 index 0000000000000000000000000000000000000000..e453471d13ca01ff8062f26acc0c4d92b99e3ed6 GIT binary patch literal 22186 zcmXtA1yoeu*BwMbT0x|{q=%C399ogCp_`#&5C%k0x};0GySqW8yIbi_CBKKi|HoP^ z)_U{i&Alh~-shY<1gR>^VxbeGgFqlGh@8}W5ae%M_UtBQ~bp($kEB=y-O7CL;Ne9ZV!jW2=^;;_ajh-fqCH^2U32L z8Li1Xju?G>)XS7< zbJZ@xH{Dbk-(IaduRHp)$=&{No%_u@{OrrKG$PLNe$Vw666v6^rEdIP)e)g+TNp@ zi$cn3s*hieHeu=M)p2{`)Gs2Tq`UM-lJ={p{n+xm;=0ad8AV{Ylr^;QTsdBYvI~lS zldP`3AY@j(Z0u`ln)kZ7QmksDh*5Q__QJ{R1KfgQtRO1|`~`mgN;V;YKWGke+Rh*l z8_nY{l1GlH8*mZT1)?O4`U{ohB@I4-M(wn~6OAigAf(kmqv@k-}7HO;2B# zR-o6+YwWM@6x5<3$b}9nbZqgxm@Yj&-YJ(D7ggiOKKZ2a&J-&WWKI7xk_j7mUeqC09VaVgV@vfe_ zVzR6R2!t;8us{wWsTEU5?mL5!Hv~@IH8Y^fs<2#7zxS5`jS2;7G#J zWcIu5Jl%)iBe2ePQ0S+k`|~EZyHj1rQ;;tP-$!VC+I6RnCOiOi>Z_}Db%Q{NZaxFG z3r`xBi)nhV5iNwJhY=AvV71+_lfBgjfgC6V4dHwj$uMu~_1uweOw9UEdk=jk34pQU zD;_MJ@d_xkQ`&zDyp&(_GrK5+tJ5wZ3`_hNOrc--N%yz2}Z@HHt zuR)+irJc_xn%?A?n0(4p=RLH*O~0i+9adrz5;~+n`8IC|?NHl5El5LW;s}9~?VC0S zK@=3R@t-(%zhfVtcz2#t`Ezp6>jg40WkF@e31cM)wCbaS8ZAFlzh%()BHB0RgOZ_M zHKVGs9gB!^>$n)K>RjfmbAmH6^3x$db|A3BZ(36yqJg(LDP!h-8&0dvr_+3BVAjJ# zc})Ic$R6}`QZ&zI^Ps2QcTDf3AF(L=xNsE(#QLPDh4t1@H*g);&oC_p2y~cNBkH=F zUX>H0cajJOuM*4T1pEc1SD^pEF%TN^Qka^kVM+yW13e_7xD60m`( zovxh)2iP8jj2!+VI5y@f;E@S|NaB;Wp3;X9*YHc4AILrCf9)Q;!|@RD7h&dp%a+(1 zdA~`k*modjY4QHNd|+f`={)P}93mFI`GqQ6<5hm4WekhNLNhoe6xgr1G-cUvm_X@Qi3r*4^c)+S3 z3Wd#qjG8vr9IG5R^{V<(MmFph@)(w2nQD%)O$L>*>&noP& zv)#8$`!0%|-|H^{@6+UG@GSnMh^2PSaGy&$eXb)L<&QE3RN`a#Nv(fKNHL5J5TBN} z!#0<*=)MpoD28#N&T{~!$g0eUq6L9GeD%zsDNle4G`MB-XaKC_*-_Fh4BLsYx*b?p z7(?6RS(<)x)%=UZl)Y=;s2`Jq%-3Y@goJOd1*r%iB|b_|orl2afCw}IgH6)?`aisVbx~yu{l4np zO9prpGN|bUmDCqLpwzv=gK={dmzKy&26sz|0Jg|oQA%Q@?pJF0rWV^B% zhL-&NkW`#|(m&?Kvw%SL>2V#mcIm^D!|6f5bu}Ju$CQ7UKMuE>DlokM&1C}p4*jOo z`W5g}eagLyjyHKj`^$g95Pe#Gvn?zIyK90$q3toncXw@V>A;&kI@)Q^?gb}!oJ9kG zX96kZ!2rP8@h3mXuRC{hdOfY8|lHzZ|E8!12_>J>)!#_xoQtlw1(tF^Edi%?Bz9h&hz< z2!IC>>3^s}uj`HO724NNNw1Ka?!H26v;m-il*t)tzy?yI^o9omtd z;r547lF>yo7dkb0^QyEV{<26QATYBd{{_UKvzVJN`7Vv& z^{dBF^YtTgxxe;RJh(+YyJ!MHawF6FdL4GWJh${62acSiFnlOROfSrc_=iFpnU!hC z`yf!W*ur;=>9(g`04JiZdGP!TP6m`1d5fyjKP+@Jw{?6dD5PfnL+vHER zI*r>M6}X%OfI8Wu(U2c^nKTkzkSYooExhwt_@ig+D0hHstf>j{D_v}~gj^>{iR3rk z`y=2-Xo$?dcaiZf2t8Uie&oXTzWOy#(-m&R?;^vlc3+y}mNN=+hW{W3GUJ^KY^w>N zyyxl@1S@htus1`Yi|!Ba%sY!6AF!v}+Y?qnO^d6_lIJooZg+_@+v(|`j%;@4a6i8a zd_~GPbG3EBi<1}Lwa$K7j{#7XR98RS-2KR4{-O`3y%qODWs#H?8p|g=5z+rB^6jbt za!hIB)vbpQIWjW88bFeuN1-BWzv#!Um}`Dt|KIhg@`Oh)2Wjf9@;z~@e~OIUL-+pe zBf!_E44dloD^Ay)0JIDOeU#hty>X|Iek3fDJ8d^Aab>-GhLxR*U5l+q$0{)xH*e}& zf-(dVy*?N0w-zFfi10^HVPKErfAl5n@W@_26A-U~)x3M_`Szcu{1c1e(L{!QUmpSQ zn8-lufm87u8Nrq76$J)^t=S&&X=S?qYS85F)4GY^pJ&@x!HV zL6`l)cxF*O0Km2KTS?7@_Oe9X% zKA4y(@uya2XPUpTd&R2`#j?U5nOT}9aj1|vij&^JfC@0+9}s(+2r<1|*A|TQ;EEF& zdCT;o@31AFivK%ry#b8->KBsR5uLE-+D4W-F7Rr0X6uQ4TeZVm;iPxM#PH-TsNzhs z9p&0zLMQ%F?8h6duZ`!j{n(z3lIWFrHc_e=Q5<|=z5!TsBKt|p!=8;!>h_}qV7 z>0+kkW5&M-W5!VXg>B%`tMCk!*!3~<*tttYxWS2NRl_&JgYE6w1*S;sfItw0FKB!b zC*T7F`>oa>l9ijc)RC@sZuFZf91#6#-vxVDcj?_ef1TPHI@&Pww`}V=RHx!M*NnHN^2UlyZ^JJVo>;uun3#iT9Pt88f{19{GsZ>x2SMT|I7 z)xjNA+WW~N{RODn9qOb5nZyJef^NS0&|y}GIFWON5kUdtTwAp%1jDMuy+c7=b%3w zO(qE%T^s;bb#v|&)cSx?X98VKDgOIMR7iPZvozOkv%1{ArJ1^{ie8N&(BjM=fH6n2=BU_jxag)yJi-{p+u#dapIPQxI7k7I3L0Z*9AI zez8pN*&zQ14kOfYK!JF6wzu;a&l&cf;8Az7`Ion1bA~l^iIPr9Y)D~^ArnN zP1qf}fmp0~BHjyAwxP6I>dDdh_^x;r!TcgpgZ0f=&|&B$g;Mm#2Nzg&`&Km;eQeEG zA`h4nqT_QjM*tB&`TCh<$-yd-t@VvY^}%;P-^U%WCE&_{KqG{NXXb{T6o1_SIvkdl z?pjCfmoVz<-nvE)V9{@*7ZnV1qI+H8GId(dG%#zR&(fkT7P`6d*O~-;WF6^p(8;A7 zw1tl=7tIvn;+Iu{nw&~=Jii-hUtg{GJ7+kIVZ9cQwyDu%{BiHbWb^R*e*fdMn4gOwgsGkeett;-mn9l48;knlDn` zx`+?lRDdTrU(QrY$%^dD%;9)903OqQI3yti;&1O^z#SfP*+;5Jx*F$-*6}?@fgpZ2 z*vzrPvC>3CUn-tGEUQlI%Q{_)(pa;1`y!eDPJf7q>-B>L7G{2C2FLJv+xiP^ZJm~K zsrbR5hHGcGgqKr6rJ=R09Yh=k=Py;<->(Q3$Y@-a)0&U|^LA|5k84NAM!JZggorLfsp z+!|sNKYH!MC7pMlNJKb+HF(}jP0+wED9M&aHc`VoAOz%gq5xxdQG5^nbAT+=V0hd|a>wWp zzZWPdf$0HnAV032&qhzbjoYnMkoU%)sfJML*Kn80CA<_7tKjEI&=3gWL1;cmqYs+x zz<~aPdDvL6sw&dXytG&C3BA;uVc zQ+AhPbu)4OhT;!cHTTOk&L`3wIqz%V9*{b?#v76B8rk}{B&VD3o;>M`l2b9Lj(*&U zV$@9ApTxpSAaL-mT0Ena4HD{%#e+6SRxfD8D)>5{EUTGr#L#jen{bZ>GtyGw3>JoL z7nu;~9NoZWwrxzk(qKp`aj9z=xdOq{8M2=d&N zj+Q`DIMRKoDXSuLpie||j#8Y3m+;ZM{VVb@$<>gY55eh)jk{Ecc^mdijz;%!JO@GWoH31#R|>% zHp2__N;a}en6o|vzw*GW~bCsxR=6BUL!v;^yM3h!Q+lQ3BUK@ppB7l#Q;)NpGFay6f{0 zWrc9%2D%OfzwbZITV76lO64RfJ$!*=!7w`0No|0qeKJm@sm`G6pFV5>7 z4B=f8XmS><{=8P9!e+PJQxHln^mDkq_Gdqiv370;U;Wmd?f&hC18-u*%Ha3WK!{8Q zOi0ZOhLMgdA1-rKxpl%0(W8Ts#xKIJBq~+K@SMRJnwOY_-V{UNjPE(j@!dQX(KX^A zlMHX)xp*#=)dLvQ$@aq^X3S8(58T{uIXGD<;`9OkfLiqcl7{Q1#(l%XI7Y>F;XG7G*TVQ61nJ#_m3a!>PlvZFC-G7)$4>afsZ2+JP#iKYeetqHS5KZAB-0P2 z&swHE_!7IVUcHkMVfJw3Y7y$ohl&!KF{?J%UtN;kGFg;76Yne&n%%3-ae-x8W3}bW zet~jk-5i;+ZK=M!twH08iRosKd0w;3`=`o_yn>ES=8p(wsCe;x_k-v1w#<^>Y7<-~ z?WOaLdTg^qR5jeF0FGK|JsX-?;8}pEx9AtcI;HQgG>dMGhBgUe{Lq>#QnGw)rImX7n2$B0FNoobzAxMVL8nE_P zW?vcVlX+tW#HNtq=Jv5ctB;m)AW4~i|$h>&@b^Z|doH4Bo%bw9a~ zy|wt3@ABj69=joetY!MI5lZiipV|-WX&i;DkKQp5O`#-;CbsTsv{!HVB<^N~1VTYs zmdvk?rmg?#(mQ>&a5FXCFRmWV?QAV&^E!^n_X|?j4s1>q7tl9~8>okc)EI|eTq{fQ zV_76kQ)6A>$PgWlGN~ehxN8jzh&{*ZG4Y`FbP! zrRh%%O+yU=MKoqo<7*>%SGm*{?HYHBj|FuFBm5N+X@=e>r;u};nrvZ3wMqIq z(ANd6%8zcDs5o$MZTG~IxrTer_!=w?5OG9*P=7rMD5oOs*T*Hx0oPBCte0H@@d*Yy z1jn`^R{Tvn1e@!#44ZJuTXdqCHpKPo3iUYHI-W{y3zP?3-2ooV%``eWsLWO~T*aK; zZmo;MPh$`Axdo)>0pg6-iYC0h*1vE+yf z+0+)>vbb!1tal~aAAV|>h;9V?#M-7+cUpFhL7-~d_%#?fxHh=)6*2@Eff0OcM-w`PP{SX?B$5f*+PCTb^>x09D@&kdc6NZY zz)|INudTt2r$xgajJ#p;bB|={DuXmd=W4h3URO2-eqZCWj4g-$h1E_=yL7kmJM7|; z`z;SPA>j|?sLCLF8$)QQt(S=yd-Jp`HEj=doPATX1RisOm|OA}-wF1zUZH(w*^IAtW!RR8q-?sq~PkiK~j`Ep79J5H!b$lH#}e>BST-Lwh#$d<*1+ye~qHm7ZS{s z<}#suB82)_Tdi_y+#Wt$%T}%;E-6%YMGbe?P>dl!DPiuJ$W|BHX%#!qmo>&}X>mojm(3m12BTIgp0h-Dd9kr=9 zOtfW(M5CKa&9d>?9uTp*k}_3oNP8XJ-}BK8&Qe%mLSe(yf{02-*)}(d)lfnR6D!(w zufo6xfU*&Sj=Kw7=j2P$k2yU^M{yhCYTBWg=Fy;8M$y|X_kJn6+`hS54@rm5fkPh# zTe}$J1Hves<7QrzUsXpiL@I&rv^J+MJe#|SEz=`3t*MYDKiESIMh-c3@MZSgRnTOo z#4!1yF>LXTmz%R^PsaIOcA3lc^vK(b(A`>dJS{$&+yS=m8x5HC;=<#C`NHISOa+{} zNJ1`4Lw#xsWc#_5vS~{aG&2>D}q5z%Wqu+mms_&iEB2Z!y2qBI8 z(eD<0;4HzMeaTQzlq;@r9*zt)sR)y?5iOpy8CwQ>W+8dm4x{5!3L%~{m}f60 z|1`8U;@Gm%SOCeKS?n`LS_#Ul+9(khZo$W+fviR5Bu~fL{FK6OL?)Qr+%P<%!2oWg z7I9^RKovjA4C<~BPKJB?3xWQ@Lru2g*w-G?=RAiAn{$f96+mw+=SiNCyYg9H-Gx=3 z50Ju2sf6RKdrYrFbSC*O4xSeyI@;TAtaY#A!wv*1DLegSSy2RZ?jD{}|_aBNI?{8NuwNWhkJ7!aDb-!?{SlG6B3l#+R z8(}w2^d~0B&5kehskKff6T8F0M4gYYc*ktQD0Dc|9a@q16a2yMde@~nEW(fh^AT{qmzPz};$RA|$-CI@wRfY(!kXnRelwBM_`KoR!`th( zBe|t#2V25-B%h#z6<9 z_ZyhyqQYxR&Ds|J{4K*hhL_RCFBR(aH8Ki0yB5rf5xfI-uy*K0-+i|YHuj?2Dfm-}=pQxf8n4uIh5mT=GZ()Nc zNft&j_m8=Lz@I>=6iu%l=yEoX9v~S9yU1-#rI~X7L<`hml!m69wfASg$;qshqA&po zDdp0&>!h9i{W>#oiblo>I$0;c{+zUZu1|EN2P{%rht?4 z{3HR6?mc6tk!5uYR(p%>OcCYdoAN>!nv_c&Qp0zAERX^E*e}oPsu13c0WV z-DV`?GiS5DKgaQYJZ~`r#@_%ULY%H^f;ZeMb_ePc3;whc46w3RPrCr)cS%Yhpi>^A zYDEX}{S+;~aD5SHP^*c<;Vdt;N1_1KdQQgQ?{FHkdnv{mDtMfzR-UusHw&8eJR6b6*_03j#6-#fp^6D=#*1w}dQPr;1 ztT_Q(#^@*>fxGkpQ-YD|&a(6Ia7iDV+nu&n(mq`ffckGv;ILAJ5ksWA)uG&8##ZAu z`4}Y#e5KN9D4m?{C0hY34VXxJe2xwx80{Qu#7}KTOFj7ivjCE;jCcDvb7T%sOnrQYNTN31L7*xlJW z*DVVqWxt^tN+KRpDjL9lew>0QsBi=T`zPxkfguj4iN)u?;ta@aAtxWP5)*9VzEw=6 z5XaMAkk19t1JP%G0`tC?36xu<9}@K9gx~hqyIwg0@y*(h|0w^)gZ$yxws)G9s(gGu zhE4S9*Ho_J6q7i@SD>dqMyU^yoFnBXjb0W_XY%^>Mrr zv=;*_=S(ql1mX&fK4uNMGseb>v}3M7)s*mp@p$=hRd4#=b%EMUd_n^%Pk^s` zbt*vylrgbBf33@~>nZ`6NNnBNXs+Ebw`YTNidhyZBn25_VOM#|caLN%DTtHzKCQw| zxUxc_&QLpNo+&Bm??je7?ZCxZyw|OcaX2s7zTm_*sf#QG377<{f`QTnzTLoffn8px z8O@fcpUH!vMe@rXj%`nc29V{{vG;kAAq~c=bR(SW@kY96Tb|f#W;Vu&N`5-p&JmQ& z_UVYIfX*&zg9z|VU{l)H4@x9gM~-m6^sFXFmUTC#!!*BQ9f64?QQ<=09*IbdJn5Z1vygrczMaO{ndwJ{RoBDf|fUoEZs4#?EYE-o8jcFmg#Vj+EK}IXP zO8+PW9Y6S*8hf~(JLJmx_;lGvxTz(+TaGCR$c@<{ zD#BTO4Lv+n+6)$W<)~?6<566ef}LXHM_=6p05HHC9kAX`Oo#(MOL}mkq|nW$tHNpE zbPb=gOIoNs?~VFw%_N!hq5pChP@mR?67$|*s)gL%7K4qEyCHRgQ5$?z+cwlUMX*7` zEvj4(rLHhI@U}?}>_!sv>B+C9^|Ym`?WZ}*0J=&BeQ*Gk!MvROclHOiqr&uecLyRd zAw`?9yW`R+FE@S@t6bD(Apz5E^%%tt!r9VBts}sA_s-5Zs7qKCaD#%^hJ!70>R+SfWQ@fyf#TWIbe z722R5VaKL0fGBwYq7;Urx=!q(tM|^mk$-dtDZ4{5eQ&#ra9z3wTsPuB{a46@vFi^w z!vGg>hEL&18uC98!pL@iz>Y{0Atf-1-mJ||Z=TFS9)hG1LDU#NyKd~FYfski?FiRj zl>Lx!H1Zkm83br!M5bu(>{^gjxxuN}#Er!|!pi6LdsFr%Z}+PZ&fY>coWf%6eBN++ znG4c)8^gz)Ei;DFW$JpCWO|AsIOWc2TfZg=JYuTe5 z9X~v3csX=jTbC1D_`AyLi%w_J8|zw}TEa$oSzAx3RUKsOU)c({YzaWaq%_=lHW{UMYYl>(E@c!7t8gr;Y$DWAnh026l5@RrR}`rrr6M+9vd?Q+fXhX3|W3 zxlu=~;zGV5-qj95riG5%><iiN^A152fUnvI!CXug^97WoEe>Xix2JE!!_G!RqILF zj7cI{U>1wM)K0bZnnb)PzD0`NS;;2zR;+c`DSFz@S-Fc_5O!T|wcQG+ACOkB0=K(& zS}7>`=cAIZu?H?^(!)QqmKbW~DiHK?uxFXN=}R?XW41ak`HCjTXHI#%Oe@BdJOkX1I2#R{WaBSCr8{5GlqL($OKc2Mz_z?Td zb#Go?scfw|UeCkHrp#b=PL}%A3W{z2ze?5PE;E9Y9F<#CutuFqzpeSnSu{=C{CF0D zyOF`^(D0W-&(HrV+HtFpV5d9`h7*BTybLGdi90nfmwdGGfe8#{ROQ8NF57_s*z%jY zxzZ~DtqO!Bi7je35_~s)Iq@oYH*48WxgbEQESMD4dfx%aU@Wu zCp@FLO(y{s+@#IfB*j}+K>Lz^bUJk!C+%=FC=Ij!%?lvuMZ_P$Qp!qygI$q`($j;} zw?2d~c)7e-&t|jFiqvU!$OP@~_BVMAiLF8KNipmpUbHDZ4+|MN4|7l=8U*exiPzX& z*QTSTM!h7J-(DjSP+1lmDtZqL$19%qF+@KFr+Kh^Pu9@1G>{B64aL2JnKM_t2Pa`J5ePF zU#S@w;;e2BdAA`<3I*oO?)ATLT3u&ZF#MHx20ByHwbf`EJzaAOf7a`|RcCY7k}uB| z=%b<*oKTM=EN@IJ0Ys!)cMaJMC8j145}-ZiN)%=-OrH6WpZRFYMpVx09_A&Q1OlL? zy6<^w!z$zl5HYn5*F<@1XyWs#+kqbl?~B{|w=5;9zBC3hN?z_dq91D|hf=LZb`Jg$ z*J+!41~R{jB=vjF0LehLn>guNH1cC=%Z|9c=`hxp-4?8m1UPV!8C zd=g}CIDl#%n(lYAs`vUg9*i>$rE~^_HXJAOUIU?1`c70lS7a+7{H{PWRUWa!nc(Lb z8Eu6TBFdfjG}U@8j67Gd`1wwp0<$Yp9b~J!s_bHSW&xh>2*M0)dj1*Hb}&^aBp`6< zWeaHR@W#RT~=}&`jVngA$Wj%?$a37?b>oPaXSs@MwJYzUE);3w<%ybIeNu z%TbxV!rE58ejIcszj?UX6L?o%mS-WX`&$Tnlbf5Y8?YAQ8tp<3 z_Crb{l(=$URX+h{NCp{yCpg#}-c(t+r>8A1C(E8mgl*wrmv=q(9`66}^2R*74N1=j z)WeP0RN=)7~zb;?(zG)dfcg2B>|-D#d!rmeR-@3a>3ZZ~ir2%B){@M+3Yi;f|15@jC71hsEUf>YKiec>Axu ztvU{GYe^0#J=-(`dc1oCy{?`nM4NP|;qKYzFctUwEM7@(uk1%eYl&c(X&KEWNc``~`t(yl>lnbf7;WK(5>24R@os}W=V*0W`M+$&B5;pzRUXG&Bhb2v9WqnNy2rN**ADx@XH8z3?1JU*z2ihKtTWi z09yb0q*s~DP9;fu!>1V&4GgA~F3crLN3MP6<*%^S=Xw{6?i7bCyPyWQb&d~Dm3y~B zUUp&|EQ!Ja2CbH$P+9H-;^XV^f3X{HIPM+$>~sDx4*=-sHRNj?JDPu+96P!@GA!>I znd3+r`q;_O$c|;%HIA;&8ItPMP;LclOIw`w_jIgpJ=5OsMy*@^90yn?({#QQi<^B_ zV@M$rX~|K$hArPL*@ew^d7-S2*WHNfV{h8SpK+CcAtm^=0K-z-a!$YhS)4Ju%}oSUDwm#O48a4B(q92`bHZ1n#} zg#a{p^4MIqPx3rv*|}q8u7Q&SVYmj+k8pK`!qOd4v^b#Id%r8wXdEVub2hcxWo3m+ zKWV(HWEv^~a`K%?YO~TcLvrKfQIy^I)NO*~z3z?2ZT18p*0miw zVYrhn4vm-ODo7AGV%57YdaZm!WbzbMS2BbSXE+ptZ1UrKY1KCOi>FG3*^9CY)Mk4P zxdy^bCbpXa#8-uWbHk543qtz0cJCI=^b0D;8iKHHj_t;C#y~xM^1y640(LkaSJw>h z4Y4!z-pcA-`LpAl`T>|91%7=z9KQNo!8~>w*@g)p!^A*FLDya9p`t{r8A)dYllpotv6bkX87JKe?Zwre*S62-HOuw+I9h9zJOpRJ>#JiIT) zxImAsFmV!1vACr>jHc|;F@4t1 ziQr^nub1oLeNP(QAORY~zeD5oW?K+&pb0k?s4>9nmF8D28Lx;(FkPrfP*Aoz zGa{2vpNxN**09+Lu#Fj>YQEk6i+%C>B$U80qpBc{Vf&xkzCtW+r) zoIaUPRs!^%HaAwpPehGp3>1MEv^@m5eTwkOY zw&v8<#c7!>u)9H#` zM~OK6ZV4#D!Dhi$r-)=(G`)adVfC6LA$yxs>geUv&Xd)yc^4a7g4JzwJNzw5-ovTk zEf1@gW$*cy12#EE^iC7|4#orNxb3?|a_eb;K_qL@XwKONy4JTXOa9U5$*VML#7qEi zFcWj|ugA&A9wR4C7p7vG;~tXRs*ijol73yWgw19zL4?`9FPmZ!3Nr0zg@-qEoJ%P&lGfYf5^tc&^$mkGVz6I%$g`Bg2S^88OZ-4E( zvlu|+S1Zjcz|~d`3mpafslEWGP`pnj85~+=t5|mCbMPBC*U`y)NoQA5Mga}6E9Vt_ zQeR}VNa74e{!;hu!y|Kc93h;!w0+MK;}_UUrmbrMMYk{nxx1-in4C+dB8ntH!ESTI z(*{EQ#aID`Cfq2rQ`~J>RsA&(!Rl7S)r39K$n1x^QzNx5j8WCXIlQQI4T6`vp;Uz6woh?I`xSmH3i`jp?BbJC+H&=uTq}zD`>?CJ_8z z7Gkc4*%zj`+LdXnNkPbJLNgR>{U!aYU|z$wdu!iwNa8eiD+A14Nn?}RD(yC@Y_2rV z`uNP|pK#;|PfW;n0EeW7W{X~x8?m#a?Ygt2e-Fu(9^*r;ZJsKl5LwA^>An26yec!} z;WLDa?fBo!Z8hS4?(%(D6$<^sq(2`vp ze*=_5w{IBVu5Q^q% z9h*%@K)Ez80Z@=`uO>SNi|I8sHdgL!GVEG9dW`lWRe(pedirK>V<4siZTk+c6)s!g6GL~*TQ+c9!;$y zIe(Xr5Z6qTz`XKBQ~`$RS?`od*8139jlEBzNPTXSG8pw5PuqJ-)6TO=sWIVy185Vp z?w`Uss}5|R;&S%l)wcngCd89k9zpgN+N@_z!e8}&@70&iP49B&03Vb0PBe{m*T1WR zwvU$-G1u6O0F^-JTJ5*ii-Fqa75W3)Fk_nA_ZvZ73lNM)0K5r8SYKLC^k#R?1<4{` z|Cz#@v-(Ek6^^;$ZE8q~fEa>$u?gtsfh&&N2k!rRqm+3fGV*iT|jC7bEo;lSmVA1BfkT^TRQONM_N zf~!0paRGW<_C)3hf5$Lhe|&&y?1Yv$L~`8xmCLoe+vnAX=N80&NiEl>__+Q9I~b^* zx&~V}mc3f^S3^~o2Ws!`n1Zw#P41pXJ7Pc9<;!XZ@H4GXrm7oh(BmIAhSV7MbaLoU zCcA-6KKM)&E4YL)GtcUIF1rpG)q5nZ6JD)RiDJmkt6i`4hNldL6pRQsHql(_UoCk& zn*zFEe*M6A(*kHa@@}^S@POtSYr4KXr}BoX?)!&xspLlex_{MIq<>GH&|P?9vb8Kd zFvd7vM^h>_rZ-Q}T-l$YNfy-i=*0k@n=W|i(r1J|yY*DAV>|J$UlYM*$cUdxigg*y4z#msf)RD(V{=^&!+5~ z|3y~w?z*KGsPkGTISw@$?ei=svyO!3;w;YAW8gWx!g-$Sqi>yzKwGJPK`jqu_Cvik zhd+A@rxR218+Xn=a>%TaCM9i?BH40qF-1;HBBObv(B}R@&JbY z-ECO*v{V)KIQ-mr&9$wxm==C>K3jK(m}nsA1`Q|pq20zC&IbL2b2`I;b7z@;zU=0K z{4~b4wBxnp^OFTTJTF&LUY`9-zJssMxoH~y_)(?2*o{qdQp)&juXM=m^_dgO3D_o8 zTt3In`ymleU^4F9g>7G5=j23E5rcK~%0N>(th<=0)mhVuK~l zmM-G(4_iwd(Nj7`_<-giM9I*Ze}_*nX5*dolY5UGYoc3oKEn+6OEZ^dnw)1?bm*up zF{8^J#rMB=-0XZ>a9Zw2>(+Q$Z)|v3a%MRFBWdY2m(D%kbDSn*(h?pIHK{MG;y|}5 zj8=T0VpC;qJCgAlUJ_TAY1QQL0!bxQ)X{}2*0=_&(4letGhlkHC`8H$mqMmF8yqH= z?u@fHc=vmE#Iu?v2PQmLSWABB{sa-TV?~8VPn0m)hp?O|4pX#x^Drarh#iVBe)wH$ zuay33EIRQ}$O+o#hja%uS*wMd730bWdBu|nwDV){o7goqe>(XsqZ$Al}c&WV+2r?05hRfho+iZE@$=tkX34I+8w*p*Xl$Yq|HP7*!-7-6TC7x-6 zfUk)uzdnEU2{?#@^V;Zu zmv6A}H%k>{0`Id+1WOR`N_BsTMXoR$wd(q?Y~g%zn4H_)&pV?xL}C5dja+B~Q?cCj zhyl{|93>FX17oyY@T$$&AzL`-i^1*(CjG_IY|*GGQPHVf&E;LmBkEnO!!bcu+bssw zb{6Gf9-q=6Yf%gW^&Whz*U;C!9qO@hW`}0sJ&Wm%bDucxE*sIL|LWf8;$F{KEV!Oa z{d7%Q$UW@isP0XjY}%>f^3b#TJxcX#NgC?0HD6 zAaeCt^%=QQ&r!}W6~C9M&+#o3sN`{zvdkX6{Yy%y_G!dChoWUR18C{uSE;UY1$IoIR_UG*3)Z!!>p_)5pH>_VbpI>FJ5Ma|-rUZVrrHh0{wfE^S>E`=hauI=9rBX1-L{7(jZb-V79E38sCY z;f!z}6xf3<_k;#VXXxf5^~!2PRNYsE=-3yEs%p?S!`B3OXpx_) z)Non$nQ3dak|d7+edyh&WT(H;RGsAxE<)Uzh4{3VU<~PF^I)qgBF8|Y!i>&v(ck!{ z=NXOA+$7<_SO2=1Rhrz;rg{_I4NTb0-(P~@6H!@LR)&eKc(ltd$avQF)FhJM0wuVM zxl27~K8{3*dq2#%<#06!KV#LmTDtP6aw8=blN!Wp{_-noM($I_=VcDC;AUzsnT z@^JgBS~FLNsMMaxXs#g9g1@;fErl#tyS3_5S=SYH;SA?XhING{3}Kt(!08=!p@NO3 z&C~Y8{-$)7*QfFVp1%6^zDJR^{y3FfS9(?MsP|(Rc&>Aur_T3-({(qG%tLl&!=Pyy zzHB~Kk8E_*nEb3c7tsAjRHV;eO*RvXf?dmHtA3H|KX`AT#r8hY@mZT2`RZu|DLTULE^s1v_l7q7qqFf^tN;wS zAHskqTP!M$33?XqIKQ!Hvly-*ZpAh?mo6}V)`r~Q@k+22rFT+tS~k4N$5<+!_07y# zlb1_A>kj+S*g`Qed*$-~wQ%L}P=4PZlac+Sk!^;E4?>x;%}^0ZvK4)jePqop%UD{B zwIW2x9uXC?W}Bh0WZ%isSRarR@ZH0E7YlLd7k59PisRjMMrdeKwg6tT@n&~Fyp9oxWR?`- zkZyaqxZw=01_-KKwwJGoEQcrGTsM3#C9f#M^KT>qu;rZ1TV0u#QML=ehlZt!xYmOxu(`o$K4c~rb%G*sZe4>%?@ zOib4gbvOg!?7i|jg0ZLKbKZeB=$nHR5fB*p@;f)}3?5x+)-=#t>P|f^U^jAgl>B1y zK+NwU|0R|5jWgn^X|o20E;mlxOD9^)c|;U2gO}(JcC-0yhp*SpyrMc~nIpD{$$RsL z_j@xcQ1int>PFeeQW!7+`#50Mr#wIhn$QC}Fk-^W<+LGPM9@td@XY7j&o9RNWlitv z4K?Hh;tNk3q0|B`>UV(fwLl8a^7HA@_umHEasXR)uVsMcA2R3Vjku%4yZl(yA}% z=4~1TxuOQsVmf{yrdBJam=>6H7#KX`RV&wd$`~jP8x-D0m0WqB<>r&0&4{Y4-;?tA zDY;Tw9=9p)#YbPr?*X*Mp2bcE2m_gOIv&@an~NFczrx*1K#jBrjMoYVw`XLhf~Mm* znkiR$R(|9v&}^T?fx)Eh(`4tqadyO5R>*wFr1fb(dzN>ND9)}`VV}Xlq@_)*zS8(| z_JHn8D`kZsA0@yHky9!2R^{YRwtKy`Mtp0A^76N&F@Kn`k~Z^C=Rv5fqk|7^EZaVb zpk!Ol{^Vp~Vl(f_{D;j@>GK@8=7e}10$U2ONckg}$%PV9By|mb$U$dSnO^t7iKmC9 z^M!?{Vjs3IGPYWlgpBx8+53R!6`=FmJ-t-LhU;^eg=bd%kbVy5BOpl)QI_bs=H9Ko zrr3FaCBr}a%8NGBmWeWw%Yql`Tt{_94jMGEhcrQ3gvhU~jDNLba>k#` zk)H~H$%8BG4G~xM+lq0lN|b<0N_P7QPa%QDjF&G{q7qfrQL`b&<#as|DZruY*Bxq| z+YZZD`u&iJdO7)@dA!z8yi!=!yM|K|80RJM^354=mV*Qk7LOQnh|dn6i{$ri2#^c# z-zQW21P3A5uRgmdt^b8;Dhy>CEFO84yH0_(g|1VtG6`)axZ3-S@^D^oDFOm;)BZwh zYQWPh40OI0G<|)gzQVEIrMTRVshjSZcTuNX<&PaonZ*weUecOz8Mk8(Ty==!t;7B*LmX17{>T45o6kp0RvEu6v+m=8ra+lr1t4_Zsc z{mK0&`R7gZ`NpnYEDLx2L%>x9rsdI$r;x>iXxkfU4VIJZHA=FuglnEQW2W z7*}eDC-$Eem(bx1Z{Su-1wsSCU<^~kZhu|!`d9#ofy#9tfEyYh_V~EOW74s7X1=_k zRLF)$6R#t^h8?>etR+KP)@?V01de>+H$FC>p+ioxt%z|AHvCZY@y9Y_tp`hj?6voX zk8x(Hkf69syQ$sTppCFw!SSnmRAQnHWpK@?nnXNycH~7o!urY)lm6=~YB#wbh^cGwz7cU&sZqS( z3$g>#LcHt|w>?IhA8oAjpLc-P7-VfLm1Mo=>F)e!%a*L7$h-k%;OIi)>hCzrDt%^? zLvZv}X846)eG*A0!OCgU7GK?YlpRL16?-v*K>Z{;!FyLCpq?4Yb?%%mQr@w*ADEa^ zB?~BSrJkz>UC8u%QxulM8!e5fur!-XZ+5jl0`jE`t#=QZ?1*n&F_H3K7j=}zA}c*A z8@Qm|K(Zb!jT2F8RsE>M2L?BJ2p1Vbq)K*4x=b<82;Bh8Sm{gdA7w+?;=FLPzmawN zM-whT93Puin^4$c)>i)%?D%N2?Y4&~G_SUb^upX&as3cjTP6hTV9lfsW3&KjshJHZ-6Pq{^bXV;+jv2Ab%bDBq7X5jm{~&E@fW6%eDj25>q@(Jm$N` z)Y|_l{@XLXmKV ziNbKNw?wiA5%yothao~UU8ZRGc9E&Nm*)gpXodVtQNsqvrPM8-oDH$J(i zs@m;BRd%teX4P1|`b{V~%b{J*RMgw32nR8t zdlO2H#IgDc->EFCM_oDgK>aWAou~{bg-)7860v7b{FULU+uropOje5`CRX@L|vf%d~;m z>DqzT|3BMDD1s7+=|z99Z?(rZv%@uz$h^Ezv5;rtCK{ezw<#-j608JUAZi95Z#RAN zx^ft)G^}aoLL!VFG5nVK)>1wx;vtXpoIH2slFYVpZn{U7zX|*75PufPRa)uWBSt+x zXF>>@hY@?f!oKd9M<+cZlrY}4v+$#k&5XF%JkaBfg2DA~j&d#}V1US*n&nq!SvO1! z3Cv*2s#?uuX=e=RBp$P%$(SfqAx}+47sojdF?Kgxt&5{vMNpG)tY4OPeDfSvRM^(= zMWsebs)0B1+tn2GFQt4RwMYTATa>zKXC{gKpdq+72**`X@HCuPU^d#rDSF)hrk4{q zgUN|cp?i^g@b3tyJ^y|ycf~LzDLJnH^lftagcCgqAC#U{Hoi#1i@O&o9OlC)z1O$S z^uv^1!u)QX_MFo3WS^vCC zTzPT`L~4Yf>zv`8n(QdMWCR`Y4c^!Z1yHEOV=v;l(ayKHLEV^VOwcBzG4#(urzZ>v7-uu_+RHa6*aJ=ITR}&Z6rVM3J(R zQ^4#APp+&peZQD=`87YJX;ECrPs4{AK6$6ph7?QDB2X44$U9?%jJ6dGQOhrEE>566xm@+5ZMj zmpK&%sAHF;eU_HL?q>31<)?4*{UC}gMr3zC5lwdL8#+2v6KXN;6L%jgV}4QG3@Y!Q z0Nz&d=0E3A;6Q`nqyQjLa$>vHQOe+!eB{P>0fEj(eeL1WwkY=}!b4PJv&pydp;611#`~T|JFA zdF&HO8UB$@QFtqxK=OGQS+&s3VV_(-;#nM5{V_=lotjex&Mcd6y;I*N5yQ=CmtPhZ zP9bF=IeQdGBsMCvX;~F-95aBZ>mF1aEnCp@Et)<&aC^0gdDp;{&R|EDD`@{e_hV-^ zMfOO{U4O6K^4b>?(CZ4>b+A9~%ms>Tc8J}-yY@Oi7laLX--(`;GV1Uh`bYI)d`4m^ zUB9umhyK0ZLH{9}fB{(9|El)Vr(hc5kxn7=)2wxnqG12i3<3aQXM>aS{v>&#^K+^m zn$B)G(A9mpvwHV2RSJ;xn?jf2!D@rl%0y5-rSxGnQ)J?;?xmxu5H9+pI*6xOsUoyW z)D?qppWv9VkO`}bhkrr`!E>=LAtEQcE+8AWR9>Y7^qU=8XxwQKVj^Ae@l2c*S7d?v z(`ES_#sZvpxM`2D5r?}>?0$dAa>6YO%0Rv9G*G*s3i?!^cdMrL4AUe@^{u*@n??9?;@7EuTh7F9r^MYak^u@ZvL{aM|tvt{Xij z@W=N~J!4Ve1lwf2i(>)Z=~zL5f|3a5rQf+wY~B&uMInP_yB5Cr81pAvTof}F7{}d% zjEM7Cex<>k$opWYgPgImrYYH-O|Hg*HQ@|eo!!cRA#@c0ERm$xCm16yD27t8omy#z z`JwaVW&S$IzSKX6E@fo)aNP}~`v@5g3x8Z^{I!fo;Xe2p24n7D70-fQsV8}zz2a9P8gCatB;7RzZo z-m;MD)_$&0Rzixq6}c@cVDiRKr~nNq_LeDD%1P@gIS9I-N_OjoPk_t>Akw$kbD|-+&vSe^B=cttsj9h&{=_GK*5;9vggt3xu}q)QrlLeL?F}xWsDg z4y8g^_W+)x`muTt%R1_Kc{$G|cCvm*&3F61_afVuz^I&Re}3b3*{Nq7h1+5F**_RN z8^F#C`wAjXm>%p_gOdHTUF_9NBkJKZaS!q0N{PU cKMxtb8*Npk|DO5} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Managed-identities.svg b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Managed-identities.svg new file mode 100644 index 0000000000..de5e4f48a3 --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Managed-identities.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + Icon-identity-227 + + + + + + + + + + + + + + + + diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Network-Security-Groups.svg b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Network-Security-Groups.svg new file mode 100644 index 0000000000..a55b053a8a --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Network-Security-Groups.svg @@ -0,0 +1,9 @@ +Icon-networking-67 + + + public:true + sdk:false + category: Networking + + + \ No newline at end of file diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Resource-Groups.svg b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Resource-Groups.svg new file mode 100644 index 0000000000..c99d7b5952 --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Resource-Groups.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Storage-Accounts.svg b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Storage-Accounts.svg new file mode 100644 index 0000000000..2fc8eb6c52 --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Storage-Accounts.svg @@ -0,0 +1,22 @@ + + + + + + + + + Icon-storage-86 + + + + + + + + public:true + sdk:MsPortalFx.Base.Images.Polychromatic.Storage() + category: Storage + + + diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/VMImageDefinitions.svg b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/VMImageDefinitions.svg new file mode 100644 index 0000000000..908c8d7084 --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/VMImageDefinitions.svg @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/VMImageVersions.svg b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/VMImageVersions.svg new file mode 100644 index 0000000000..a93a8aafa3 --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/VMImageVersions.svg @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Δ + + + + + + + + + + diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Virtual-Machine-Scale-Sets.svg b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Virtual-Machine-Scale-Sets.svg new file mode 100644 index 0000000000..90fea8cf2c --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Virtual-Machine-Scale-Sets.svg @@ -0,0 +1,9 @@ +Icon-compute-34 + + + public:true + sdk:false + category: Compute + + + \ No newline at end of file diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Virtual-Networks.svg b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Virtual-Networks.svg new file mode 100644 index 0000000000..e2d8868d8a --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/src/icons/Virtual-Networks.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/src/image/imageBuilderimage.png b/avm/ptn/virtual-machine-images/azure-image-builder/src/image/imageBuilderimage.png new file mode 100644 index 0000000000000000000000000000000000000000..23fcca251b78bae7c90944ad7274877d4afa72d9 GIT binary patch literal 211739 zcmd?Rc{rQt7e8z|-H2?^~nGQ45|{87fb{5+#8mv7#4T%6pVA*o-_=TGt3jc2d+ z;GYJgcN+E?RW81+{Bx$sxKDk@(;0`sc;$0PcH7^$RQ`0Q)RjwD&Y!%RB)PcltkaJ! z6fo}hVp-vh#IJzmz4 z672sz-ErRF*txCe1fM(b>G8&!zy9gST-|rC4Rkm+zP)DunZZ-Q?SZRKXBqDM=Ij|M znL#K@@MWO^da2CP7hk^T#5dOn^(dt3ENXuTd~F`4bk+f1 zU($+yvbguLuiyt;5A*B)-@PJ#&-8=LAkw(KU#FxSpU1XonR4YWHPStZ+H0L=#Z%k2 zDgsau`}FD);`t+@z}JWW)2SD@T^Tw3pvbZyZI^uAU-m(h0XgLjl(SV6e%(dHzyS+m zIoI;0m9AX4I}$eMaXGt2?(6#rHJnP-xrjsvbbkMG{6&|b^V5p*x167=^e`5NdNFv% z^n-uq?HaF!H_Q*AHW;Ba;R0lEF~|qY9EN{$tNyUg_4msu&vpUd@6Mh%+4yjQT=(0! zYqCoFap<%9xiqig4=TRQ5u0>K&G^;H7Gu`9J8GcD19EV1yL86y4?qDURwKM`2EX4C zxM`Sa+jB+Nu7l8}dU6_%Jq6}%{1RV-^y`wzk&W~a=;)H#>3!p3*IU|tXr$Go*U!Wo z4?j?uf{`-|PUIN7^`vCg^h!Spt^7b5G*&1(@r_c2Xow#1CA`bTMLXcVJbX!l*S>Bs z>0Q-34B6fpC0p(RD7AP%)tH!f3%Fv`vKDauOO29(M-ELtpw$rj?0>_=@K}`PZ&f7^yj8yR^xWE~cAdMX7niR?! zJ9kKDoCh}o592Tv+-MlQH5*XNCC$wTT24QZOtv4)@E+?=wqad1rnhryV)9BPIwj8kRNO%{+UvnXba z&c;3KK|o%QdV$>(dD;K+78bHt@nmx(-HEqE9q(n0`?iaXK>a%}Zx?eh)#3R=c}#o> z?b%7$l};?^_9KyD0ExdkehUR&nksndNXex0A#`kQJ!8;#B(mV7duBjVaF4v^T#EB> z&11t?*^BKn_J9WjUOQ2#nWEA5!%BuN=2to}^5->a)rjDAsdG*9*%UlAsWvKbHwYBV z_x;9(E`IEdHiMNHldpyq<;MV1HQ*N;2RnAbos96pnoN2 zKzrQMA^9h&?t1yuB#zT7acR-w{Kooq&}x+X)t<|ElFKOZu0a5*p$zbmxd>s?rxbCS zc|>#5$Je_IxJeRPUyF60WjMDFuXqHn1|d`r?8qgY9?EukKre4(xw}S(X{>*I)0=4H zSX5JgCUrP;Ce9%3?IFACS^~V|e37(HLd#%hcX{21s2;1_Sx|Uaf()gCQnOC+O3gG? zclV!d%Ka4wWxZMLx$ExWWDW9xGYv8Sre^x`Y<_ zL$tyPhy8t8wOw7mD+Tt)+^E$*#7h}pe_@t7-5iFpO({@cXw*?99uLR{g{HcWIFCXA zxECMNS*d5FjwT;m_{NK>4V1#`Vmj-b;`9&gD39N7%+mSr^wibZg3JP$T?!5J^H=Oo zqP&O~^a*Dn<+i0{YvO#+6mXAsB7&%3*r)|j6a z+V+FjAE!2Pn<*9Pzc_DOI8{bJ9>w@Z8w4OS0On(FAoaX!=RMabrVHk&bBuc1xt8@6 z;CB~bvAo`%?e5YD-&yEdS`7q|WOrA6b-^q=xaaIqMSEYQ9Yjx4V3Yr-X(fu$O7KFL zE!h>sni0kpVH=5aOjMr)M73BpRrEZ3x{BJku9i(^u1~eLan84C$4e~%v|Mis z7(^H-U>O!r0L{{<=oUx0b5Iv%-E^!DsT2rz6vg!F0p#|SQ_Y_O0+h;M&^uKL%_g>< zd5Jw0?I+UhN~Pbz2HPzTr!IxiQUD1(96!6^VY&o1&&Gb-JA9&OE+Hf=OMA-3m-Bs# z@=!j?HHrJ_D5aiyDejv*=Thz8u21zq7&=rS!M{npg#m68GakCyJL0V3`C-@raFftZ zdsVh%z_z<--lvFN+JuTU#&T@YDhFAz!%dP5R%=ujkO@9W9S()d4gF^jsKwQy~n${qkwjft_%O8_dMT|_% zv`Cwz<(|kf@`5xZj&rlUde&Ojax~tnUSY+-XAcu9(yM!vx>EpjKmGRkU~(8g-?JSM z*duAPix4oQ!Y zSz*7j$bWpqT21qKctff$8#iKvvFcEq z9rN4EZ!(EYWt^*q1D^7an)_oVuy2!W3Ely%zH_ZUT7}vLu}>C)c*EWy3?sKl*JMky z`l0_qi^=%E(IUr0g2{BYSomNp2dTNZei*ulr&~%7j2iWwB*#PV;4P##U5g<18D}pz z2Ni!0}E6&>Sk92*#Ttpm+U5h zh?C=nLuy@%<|4h-;2g${P2&TG;q&p^3(3upCa`O`iOE@qaKhcW7gQg`Dr6Wa^Bi%X z-`)Bj>krY6mShY3^yTDdbGbaJAKVQO-J=ZQfaVzD8`kiRyH%+=!NZIGE9!1*BX|p9 z{(@f|@*vqoWM$^9$XcDQIPH^A8aZmE_-ag!nD`d~Hvj&3<8K^{QH>n7VdoBF)TY`N zLl&5GuMJQ(((N(Z>krjS)vMCdy1$XaK|gqxLgmx(^_Qo5E5MC5aMDt|0Ex%f-{tg3 zM9UY~sFZc<2L~Kv}zPv5LjjB zpO9bhQus6YVocTGCl zCrWvykGd5Ak2kqO!egM{6`GB%S~c6dM%R8b8V!WV!vln z4gNqzEFat|&;85E|N5Uu=FJ8m-AI?=irtB6n6;zi;^pIO4AXNVw&_*XSVM&iow1Ra z`DCfrR8bG)^>=rb){p0azu0T0ey~#xN*hwMLBY;Nh~sXcN{HTbvEpitBnG64!`Jpm z;?4OO+{m@jKFASb%2>RVCU)_!T-J@)19T1%4I1cQ?3f{4R#@6@wfoLU-g2 z8fnr!_wp9Y0I@Fr0zs(Wf#3x~C6pS$FHNTH{6YH?G)=xA@lp9%n9&!L5nYgc8yv*% zTKt3&Pl3Agt>EQ%5rWK1{atvAj}*z@%pyZ=Q9l^=gSO&(`0+x6MVx)AV$=F{?hp6B zNhS{a{~Hy+J-LGcwFJ_l8WlR1Jsy5Tx_R#b&zx#2LMc=KsDALJW~nzbzrPIQodM)o zjs8a-6OjdV%cXlAi>tlcN5~Y_U%MQMb82Bh%^xm4k-&O~-Hts3(GhyvntSKAL&GgI zt{}Vp_t+i(uR;{uEEXPvIZ5NQvQuiJdoRZ!tC#;$m~xdu2ILV$Jt6Xg-Q1RCFRqY@ zheqjtz8fy^_yf6ahhQq7nGy!+pE$UX_0PZ!0RuNZwK3w&8sYZrXKz@*n@{W;3jgS( z%ru@-l!fy|@AxA1;v8=|-rM_qaiq-SS@Ks7CKRv^vRb~GCI)Lo%PUx;Gd56yBh`8heV5Qe@jx)Xk%(|RRruIu_Cc6k2UxPHs# zv`K9GLD<0sbp-9!Z6ccFY`V5~kqK%P@8gZodmX_WNDz5+`fQy`Q&5H((l7~25v3%^;?;TiTVcc)yq}0&!||NCw}>xYysmon-?u!p zpneQaCcD+5r+k3SAfB^0nV{FQ@xAg|K0Owej}s`L)Uv1Ihbxs1J<1LmmGJRxc}nEL z7=-lPl&PFPf6s0vSd9G({jjYjPkR>=7OZLF0hV)h6m?y)8sOf%Nm5yZPbip7`P`Tb zGmIi3Dr!a zVD8&c;lM`6T+wR-k&sIwS*^{7Ru5>tI&fd!&F?(o!GP80{Q0+wQH%p%ewO;Yrj8Zr z4b^|bK}XGQ%0C&@7lMqv9Osx~{ad_gkMga!Z3pG_H+V&U}8>QnbH6guNEx{I2pIS>*~9cJ(jxh_4)o!2T_X$UGlekxyK_He~A8`Ah?l0ecA?wbh-bIuQAIzs2r zd?0-l^&QA)N#*%8;;%ou_qP|Gfzf}^C)qf~2layJar)U}YGq5R^&aGFyLcfYAqpkO zPsvum%Xyks2At(ILjz3%P!{)OBo=MbaV?SCi|ZR3UM384j{M_uz=ywNKhMUhjdGhC zq8J&^mo^epSwOr}4!4AFyX(-$?S;Bc*|oTjwyZe@_?5feie^+w;p#ixS9lV_T`qR~ zA=YYQz1l)6 zQrcf()nbO}ID1ikP`ZM)bMlGCk}FBE@7_Q&xs|Wlzp!=E_ zTNB-@cqHPpi%HPTP&vI{5&%1a?DWu%MdowyV%(x)%=+9zH$-|1TDv6~aP*oc_ISVI z3dtSAHdl8fsSgngX0fqr__O3umXXhN$@ElA=?u2uF@anwXPoGK^LaRWbqVUNi+5l! z)0t_8(r8pvhh}n(SqpHgWo_J33I_RV;`SJ{#T0|$kW~XgVQzsu&c5EaX3Z~6|D-)J zrq%Cj<~b^_83&ah{PY^^b4Al$+zLF99*SL@X<)QsnmXe_Mqp>d#2xqjAXzLgvT=#V z+W8Wnm8De68XsP3HRh?g6D|?23OJUA^y99IYUr;48OECB=ss1W6@p}CnD*%kc4e1h zr3C)ODhGGRc?6Z&gc{JGTlK)tp7l37ZIYpU)w-BO*PuT+ogYr%RGpeCS>fP9XtnyE zvpNsEION!;f$)^x=3taMV+mD)`!kYAN(D@4)Gxk- zJ-p@V4c{Tc`l`%IHf?AhsK^g6g(K-2j_Hbg?U*n5d4HRPmitO9@&a)pFgA4qAw5in z`LYyHDj^$mcCSV){}$X0eFyNPVkt(IcrZGVEd~(>TUWC7VNsq$QIS=;0f~E?6mqxF zhrppf75CI@5TB{rs95D>8S1z>1kE3ZwTosoM~&-Toj*DoLeTbv@lq0-LgbuI;a>!` z%yMBeemG17N7i4#nWKAC{pxjHJUEZGRf~U}-!d&plOGheJQ4EBo5K`Od)UlX4p@O? z#;o<3^^Sr$F`&z-5q7PA9$Ijf^zpmps4Gl#I-vYVdFRc{UvEiT;pE_C@l*7$Rz?t@ z#XjL7ZHP|6{KlY-E4N@i3`D->GmU_!MKKZsO{IsMOSPA+)US?w(%Bxsa^=){@s&syP*gs{g%jYJ+Fb?|G`;8Y3<>#rd8I z!pa|95Q*h)P^D4AZTZIwR~rq=-iqcqy3bp_7{hh$UWPYVc4{PcfMLNaMTO!tz54vnCIQSiZW5{g_7%C!+WMVTAQ<6gDlOWnMiB>~F=sou>V3vyyfa5wsLU}| zcVLzWcdJDTy^-IdX?H<}lvU893dZ7&^WPoEyeoe?B%bCRh+S)R>qTqlnl{M+D75He zA{A{ix{=lCp0&eU5ud5DU(s!!~0%Ofr(PXco^F#f^*i5e;CGl z5rk~wRehK`W#Ub#n~HCu?wB+)npY8SNZ!D>75P0&i70QmMkN07MWIE8KI!#s86O!> zfr#CbBGST5;XHFxi(;&t(wTW5lL#K>!fXRa$uUtv8@JuiA~Jg_}OtvbB0S6?OZEc_S+U)|3e;hbR{W!1`^^2eQ4 zgi8R>c#3kbSAaWC+?O%U36L42yF-w5S}-s121a~^wNDZ)?lg$w$_cL2CC6@Ng2^d( zmopXUh2_l!OrRj&V4ho>%It)-*^h?wC8JGXeyycEgie}k3sduN&wne)f-5n6Y#ati zt5F#?tXN2Fhh|`$bEJ7C30Jay>>VJ&8GV8R-_a!x(4j85P?i9T-kC>f>4tSSx@Uc? zQdZ@0#&<~8i2b8F;KN_%x6CNpstj@C^X1@_%;vxrbsrX(EZer~z!(Gfepfr`B{k`X z(io_bcaR(0CF!$bO^Rr1>U^LmEP>%=R3fHyBQyUz)WGVWsW*3$vWaRSia|*Y($^mk zi+O^3n2!jDxeY&q1~}>rjVQQQM1;l6r!0KN6{0}`yeuEUNk=F2W#G$SL2c7ao2&C- zhAUyCo7&q0E24(&r#t@IpEfL~b~w0O6N^9VOc6ex;2Cm5AH3Z) zJF^y;<_xopN(TFyLJnPv1M)~yIQ+;pZ_TYbyMKF2B^3jp$oMBa9mSA|<-byZr27e+ z7p6R$-#D_PCg50`1h(mBYL9d$v1Q39=m13AatvzW@2#L*~3|eQ+K#7JMy};{QmJ0 zYQhXtpR|!RGg)Qcwi@;%KEQ{1PfK796|{YsV2gH~iRC|uV>E-6;ocKzM#ZfYSi)gz zHdO>36sT99eGJSf!lz!KZo_v4A)36r3&fq;Zut`k-6|cpyo*W>?EWh}? zSVpkUt<4EwTy&OaS>ud{Si;A2mMcMQa=4OgIDZFo4ZAyv(bg4$OYq>TD{^g2=HbT8 zHms?4rPGf|;gSNyQM(%yvb>(c`DK33%yrpsL;-_Zxxm|<#9luj>#uWKpEDxOeHTjzw0OnDr+P`#QqP zS{WfEIG(lR$edAm06WjE37Cn@0!F3PNz_SbLd^>7YTqi(6tJkn`#w*PU&`qR6MIzV z{w#Q|E^VGc_TUyke&y;{jmGkSvtD0vra(jYA6)2FpilkT-)yzv@YHBAGf@f}MzQ1)c8a)dS;Lk7szLHy2`?oXMDz2@FejaI;)d&0D^j9&Ue8s zb#IjBZrZ(LOP1FQ5AHz~M|4Z}iX*y$tzuj#zT&u=^^D9rF^9@DcOojD`0Q}6?ujly z?$C5_DVC?AR*%zuO=E`hf~q**<5FB(@^o1t1#Ru&U z=U!ab%$BYZgLN_J_X<~_Nz(K&82em;1Y47l22ANAK33%br?;BOS6EKCnX3jtDjI;u z1yaimbjSs=WL2LTN*Sw1wMRA>T1zdwr(Dd#w88G<(N7knaK zN)$3>zD`>O1V(3h;Rx+E07xEO3(D9;fz(^v-RGDC%fkPz@0MMPWI_0SUM zZEoW`e6py$vKNhZNm3`W0-s25I|HJ_OlNjU`lD6|sX?*bEX(UNV^&~%R)Ts`jhSI# zcqK>Ge-DiP2FReyqnh&d@V^YOWQzBoTW-Pd#WZ1Ae{VsSE8KODL^n^bbbMpw$OaPo z2#Z_=0CC&z8Gf#~Pu~DWxLgqxOh}1NaOWN-D6V>6AcNf|QM}I5(4Y!_)^`114VX_y zbPeh!ePS~z*8#>~@}x&+9fa|_srXX%%vt`AzY0ccRK+VTbnbV`d`Z|2hZ5tsjexG^ zz6|pyN3%2fO9tbrBg?^sfUr(nCI2v^fsk!m)wlcM*8D4)QG@jAdA0gX{S?LzX+|`= z&Y|h_rtRaANl5T^yJYKtvZn*B040YwoRio^a zYho49Q&j}9s2bkAO+|7HvT5YuWlo_niP{qz2#2+Qti@U8hwI{BUq-3;f(fIk0NBB^>$1m00_|~;~Hb*!`3w=R>~#k0%Rt9 ztD*RSn6S>q+W01SDX^os2QTT+HBz&7Sm0~mOBlYt2Cux;!1@nu@y`|@fk}1mdA3dX z9>&>2YMp|fn`V>ETAz<#B(VK6wM~)WtVl0KM6)ip{rZ`n^n3Y0)*YF;DB5t>sF0Sm z5wlQr7C{5%N#Z_PGfb~*hrroWqt_A86>G` zDlnSDU@}PH9$m&gA@x!W4V)$rQI~3e%In}A1~9*WO;Vq3q_f%Y1}vuJM>7I5y?T6E z<6%iF$H7T~NDJfzH!OI4yfm0@Nm*@vX{c~cfq)AmarFnnEeGjX0=xSs9(VX=x-K~( zf@ClRe+t-`uo%m0>!zeN(lRkuSJ8H+o2;yb^87Qt|gomZg-M` zKawK7z3#Hx_AB4SAwvd`pT>GVXFb3`^ohXlA9JRvhdrhTuzV890mBG=~K z-`dpp6tg@@UOIH<&)4S3R>IffntKKx;lZ{f=CvqR#ZSETIVSYXbU@4Y<D zCO~xl(p36bxHvGJqKeDhHyBB&1>OZ%oVH=D4y)+^zX1-|bGvlZ!BrIFsHSvb;vu789TUe1uU8Q95XZ>y z$>y;RmE2fKyfA`M|CN&0?Y0!?{Ns?N<-R>bZT8vM|9YM!ri^PdYw{lDt*yExhM+Qh zB<`MzVmL5q-?Ut3*za+;2eI6w@PMk`?87yORXR8sIH z)uxS5#iA+{Cd_zO-V#?Vq{+MiD1df0iUR)ChKJgIS=Vkf^fBgohyMw9`1BFpICk#> znWJICnfx2G&+#)25bj}7jMq-2ht<3l61z)^${UQ$0M{^UagF8u+}oCrco&RpS?3J) zgl=R#>%w?*dBl>9a3_~t8KuFMR1oAW*=)OyRYTWT`u1$q-R`P?0|&ZD0xL#7x?1G^ zOugm9e;jFJ6;e8^co5kgmzuZ~=5+l#$7k{)K&dGhC^fA}eSe4h3GnI*qGypm(ZmS~ zUfWz9Lxu)eLAvB54{k^ODhn$3N9Uc|tL5$3z_g5aqSqE-tMN>YPUCrIo-Z3+zOfT~ zqS~vQ3yQ7h!_cqHQ5|gEPK|I zv*S>J;|>{Of*|HqCs zisAfJxdx}(Y(6x=<3k!oFv;qg@OgzZHcY`0t0<5#9o!TkFd53yCKR^ zf_fC;cUH7p8ESo?)E}R*7PVZt+c?hAV=BeS2PRCahKFWFNPRbF!Z0?=PMDryT57O&Q2qf43g2BR1cB3?3KY;}$a9`#*eAq>Oon+I5;VW=u|E>CNaX&n(8+EiS0<658zsbgfVTi?&xaC8U@U1d-WvKW_05D*UX^eb+l&uTPxiZ@is?5FqcoXRXaG?y1p1hjU%Kj*VzJL;lr zM2b+}pLXT2vah_)fove-YhaDJFdeQF)31^0sUEec6jqXmz-}F-4?uJVu2eb_!*bz% zw2jkUO(BOl($eO{NN>E~_qcYv% zgO*gk6*lr?H3{iRmB+U?i-=2Aq=G|V7XdT}J#l>@=zrPQg!{+44_ zQ`6BKL|cROu5~-9D5PCY5V?y|aF!pF0C`e47+b-FIUD3OodgT&OiKW?fDU zS+Zaq{rloO6!(Jb;f&RQpSHTbztz8}X@fEJfl}`SHfjs?MdoBy74HcHrbunEKspr8 zy@9!8<_{<>Az#}5vDCh~OuG*CN{fJPw;grKF`gk}!n-l847*8Bzic2WhXkx}G^w|; zxb~_bbZFuu5FfA+*1SO(ZuROhbJWz82?16$igXBT z1gXz;rQsX7(nvyJ4}AYwzz3K8H?*=x?VoEwpzs$t)zZOBIF}jv}hD zPPAuyZGT&rG|%b>b9-bBoV%KEb!=nW zr==zsJzf71VW!W99g^==fRZBM_rcg@<=VS54J5$m)zAQ6ycm=QY1%kfu4S8^cIOGz zSZ67epzZqTLv1&(s4vCYYJ5UpwHKvIV72CsLUrG~g_&}iOj18~fP3z+duc9lNY(mt zz*?1oRPdwh&3TGMs_Kqj$-oEp19jA$m?<5Dq<*GX9Bq}gii^veudlLTU%3K4qmRo> z?)Q?XGaXNZ$emnRPjm>pwRl?PXG+zJY;qE9vc8fdBgm4*1D_V7pQk`=v0-+*{1n z9xsy6N*&A7mzvlGEWDORu2l%PRkCohhkq|#QgGU}+a=wmLRIhmG(YzX+KcZhuyE6dw%j`{w;kb#K|S7X&O7njyNE)87BrW2?acGS zS#Gt0OUYl?(`wf#@HtDfW6)R1oGSz=PXq*L$<7E*4G3pRSG^voErQ35K9=;1_ZkDHejgc%6^9Rq4DKt(Ro6A2Bf4iz7sQs)?2X~XvA*2-ubQGabh+Eb7#LerQa7Mtr6#Ud(ZC2!-VXqiLi}TA;AnCLJideI?heTAl z$RDxbo}MF~;p`@Z@JP=$X@CqP+Co2+gHvZ?wXHVW$IKsFC%m@^x(%$kWbJYqtMrPnSZO2!{ni0PQtw6~Jk&)mYwSGK?ftBc1u^51gw{co5Y{3o;i47+=n%ZQ z|BDFAX9k6%6>6KtmmBPQimLe8+S5-waMY7j{`x+*k!YXu(ehOp!E&1&?`LEWeoa=m zKexs9T%{As>)uPb#VWwNGnPN;40mDz2BbcWx;`{Os)@HO$+}fUB1Tl_fNs}1qulwW zK27@ZM3qj{X89@HE(FlUYx2`032b12pzR&#c`SioAC9xV*r}^?*j#O@S2a1XcV>$B zrgZr&Jw;NDXss0__IXUE!?!xbOZ6=>`0#I! zt`9o60ojxV@gT4$`Ci1*-{!Q%VnOD1q5H+US%&DzB7g@VXz|HCWp>_Rf^P>+U1_2Z zA*Y7&FRD9@uFc_kE4QC0LzbBS?U;a$|6j#$KwOhP&X9r5Pwq?Kg*tLWnwXSl(j(_J zt*(%Y@!`jT+~|5Est5JsNFU>~8#Lbh2N-zEFGBnr?1%+=YT^w$-H26Sh<1J+qRXSJ zl((W_@A78O9q42L{<#Kw0@|z1UCcR=bGN&^3OP<|)9gt#TR+8F3m;VAds{bhtXC^k zMy+B&wY{oD@9J24zCFOb%i)(XB?V6Py!0 zwJw@v-m{BPHL88eTngzay|?m+qGByjU$N}L`R|0%zPQ*nK8%?iEw7Xe4y&(c5xfxF zE*LZiu5?PL2aJLYw5%sbNpKzL$DlAL`C!g$51Ie`L`Y{TGf_MxFL1(A>*{39m{at? z1AQQ$XJmp)4gQO8@tJ)_&6SRI^YjDA2;Fi%`Hrxh)a7)Y=c)JXt^~*5-7hR2Oepo+ zV7d*sCRw7(_8wf=qabsq@IVxxQr>|tuEcdBgK3=XJY+abK}!=ZVa6T@RgCsx-vI$?x!Dgi+P?$@8Mcjmz5Ab8Gc6YF0xRJ@YvH|bxAUt1$UvHL>* z@)h>e<0uySuSFty@2Zh*yJ@r7ia*>_9DEd{sQT$Gs^^YP_;{%IB!Ip}!ur84K0MZ> zHx2b5wL%gOtu=U`I@9;P5F?!%)D;`*Kn&~uQt<|;n?wPZb`B8u=B_^7+{3i_oLE(3 z`TE@8#RiCPTi#qoy+Ued3-<|VxfMDBp$Jr)iUkIxy3AHQ!Vqf_gysC3Y$D+7YM5|d??LE&to{)KxqJRU+-X}crLQUh`U&Y z^ANuSPtT_Tt+V&%HcM*`0KHSxVfeUj>;Adol5&e(y-u0Ilp?p+5j!aA4K4srSCO0< zgcMlpNMPHG4^4V|)muxe2d`1ozj6*Cq4U~;2xQVG0%+*PYbB7%Ym@6*3%Q7%cw_k@ zfO&+7)fzdz_7}Y;4>$iqu&!*6uqE=aA~lSAyK3USbYc75ct+yHsRf;r@FQnOhhl(Q z?`IJ+r?*vfNJOUq%(Hw8-?T+LvI>)U!$2&rMmDLMUp-{1+$s{gZ?z71bsq_zG=oKMs^tisp>2;G$)#&?K|aN^?|V}U}^!HztDanWYGh> z#*>Dhg_khv0|nIi3ksWand73lo&4XO+MXTBi)Om$(CQq12l!{jkSJdPFIi>umY$aU zH*ye80!2fz%Y=W{*d|WZeb^cB+B5Co%g&w0x)oZQf_w7MHO$1jJ(1mLzs_euQ}u&| z#nara)#Zz6PkFWf=O21jpYI=n@yaQAEh0Cn z$(hd?nGSfh!KM3@*UufrIDlrx!(z2qe>29eeL?kpDd86?CKze(6uxa zZKyft2JwUBLG~v7;2CN&wSn6I>^FA%Nh-itGl!QmI}hKE)aLI~rUZ_H+>0Qqy=f}l zi?vf>Is7+!X}^9Wo(JmhLCmWw_3%ZSv2+;ydLz?no)xaiYTVFSuG{ltOBbTgRb@1o zAOZ=V1p|@wwy4&4DFMJ47Z;BKFZed_WZRi8dYr{~0I+^PP_ETmjy#ar{l^n(Ty zOg_Ttj-g9DoNmfQp?XY{Kg-(C;k%9SCoA!N|)1#jf!R`gFVj#~W}IYaG!2 z^^e^?xFuWq=j(ZObTfythJ?5SB=n^r{esLuLMRQPIMgYP@*Z^vatQdE?j$5s4eWoy5U2VY!9#_K{-;;+{WAFyqd)9a-!9fk#kL}eaAZZMo+weLKQ7H z?Av#l6cFnH3NoBMiO&FtfOoP$rJMBGKv9#F^I3yuA6D*sa!{_)@5DcpbDg%YGHl?H zfBgdV)UcsSOYfhE-vxFvxQ6+7ZYIIf^hPY+Rj?TWVe|#4$}RQ#y}$kiD10YH5kL=s zgD&7MqNcxETLW(P+s{C*$kjR$1Q@)E0f2n-cYe1$nMMcTLDj1alN4MkZx=$t^orQn zXVr`ct<5HW$%M$(xsqxMjfO*5+AwE&PkySBYZM0#JlbDRP!X?&0Hpe_k7{7y?*G5Lef)oa zZ<8^&z~f$?z&6yHzIA9+y|-ofe;dmqZ#s9OX~@zN&}Y6mjk}-5q>wa3f@p+yS*StY zW?}Q*Bh|2NLeKKB;exvrK4Ie7#w-10M3ueE=}E#p^{+_0T@S2eu8 zGcI=^`~o=wu|cN-GCV#FIr6cNy7`{#>HjTvy!QA4UvRe2>94k<|47~{Qpt=WY@nFM zadC0@TWAc)gU^*?O>361LE@Es>{DJ{0wx>Nz@wXu#Ue^&Mh}guj$O7V(EFim@Z)T$H$Fh>MsLQ+xhk71>--Sq+&-k7sFe|gCFwNF|6ql zIhjF3jd`KLPbc+KYIfg?|D)?fh(N1<^?P;S+9TBP7uLNvd{HpZH{aWme)!?$E*IvA z?~ZxZu^9O?2XuduiFe$eez2-uLQBT)n1enp%)UP?A(29Y!2jYr#nkaQeyr)@QNk!^ zv~ARFbaXUu^k(DpgOfCGqy5N()3hY{L26mP8iz?z)nv0$?{q58-16&rUK*K{;Wa|4 z7p>Ly)&X|}C-c_Jl1e{G%kr^pVJ6l?Ifk)-qAvi7KK;FjtKV6HI(MhnJR1>uxoaXM zpVPj%R6F~zF;emH<~&p7$e$IDz07>B$)AjPF;=&&#!)f-AcptkU_Gc(V)9t2i-;C? zEqpvSnh*-}A>G7}oUwp!d^7o${6uJ!@);CmWUz8@+GzsfTqSjg2~%yZ*G z+r8}6^D&XUP(AJq>mj>0@lf~je0RCuD$-7zjM(i?fxx}~`qT4&9Q;V5 z;@{;!`nX_bM@NTOnHm8%<*jq4WWLP1^K7ti>3|iZwCo&kUSxi@Jez!`WTCUUG7VYU zQJblSEbW&jkM+kVa@$Jt@r|k=6M80Vwf2_JpQ!F@_e=8yCYiQrl2Ji}}z_xm*2dia^3cl3VR?y*~MFa>Io$v^8FUbi2xMc+5sIqs;Uq_lF> z%U?vx*gAh#S$6FoDsSVh7&RDRxMj+Tj)ne;fw47Sgj#7mfJj`rR+de^QkpL<%Y@wRyb}yi`X7%hGCfNcup_|; zo;_jZDE9{8YMj8dZ~R}%kyWRQaU8G(G|(X{i^IN(Pn-wF1C+X?$7&I+aCzI12-u97 zbtS@zp;|R@Hh5@Jb-{{pHJahHlaa9vFrxZ)fyrD2Et{3EiG4Pjs!eaZ{Y9kV%T@18 zQP-Qo$a*7?Q1m48og$YvHOaQF6(p_a4-=HEzrQd4J3W;m&J_S&!W+6&+iw$skHaoo zU|H8!{|@$nKxbDSLB6Ke$dDYBsk6Z>D5#!x=BD53$;Q8+Q7sJswL#iB#bF0OK{}ywLspjmhxJ-SO-Drfk ztNH}ZqAGG1F{9tlX<3??*9y?LAO64MMiyh|a9e4%yV1$?((KxI9kM0!*!HlE%U=7r zYZjwXNu~DEvSSdB&Zt1b*V8rpOO zGD#c2juTY9(bq|K`aS_%ED&~&5n|JC*%5HAu8l=3Xl<0EMXpW$C7cle6N4oSCK$p0 zYFt#bK<^bm!0>zR15VHi%z;?-HjFx&vNBw%4}ZdmuI{43YcNC z*eKH3z;w)Fd!%(R;5OrT)2zDB2Aj?R&bnuyWPwST2kfxMG8aJ5?=!w2Xn^_Od&B%< z{Xc@}Q*oOYqq3&P)&s|2u8-;g2LQ0*&%u&;InO+sfpiz0hD{uG)s)FNW|?b~x2Xj( z(A{S}MC?CWm4?JTWrA5PK)ms(SRY}W83u6kJJWy7e{+`-p`T0sN9b&gmAy(8$v284 z%%2S&rIrcg!Mn`XuUQXNjn=q`V4t4@y}Ik=72;*k7<;gf=47Pk0MQSQD_MZteh!R< zEy==0U_Jbh?*wkDZTOE%8vWnPWQP7(&PN2xCg5~Dbh^$48-rHMvfXvMb_V8@%#ZVv zwqXIVH_7;Qx!bQn<3H9z>25y&LWqlE!~wO?t5GfEPAXcuKOKp$cKr*n`({FC(ZdIS z*GDVJe0 z|5(y-FILMrJj^Cw+u?_=Uw^1}IV_636p5)?+of?Yh&=#hqR2CVzO=N%(5Tcm8D2H1 zM!NR7I*tYQjN@UwM%z`E%yomU!azdku&QVQIJd=-o&ml{=B9y*pp4e#vh~clXSr^d z6LMZLjz0W(VE%g~@K5>+nLR`3yW`Hw9Zk6=T=!0a>lL0zt~p!%{~H}gUcRjc-*;?7 zRibkk2vmn}L}Ch{jSkdp@Z}>q#Deu5t&dT_PiE<>Wnf>Biq9B-oC3n2NE8O5Isnp1 zcrkxXg)B46_=?N^$trlbEH%8Ymiq;g&fF!`l!*BpQAhOyA8|v*U`plw4Y%)yldcJe zMU^4<07^%F%Y&i2y@?x7DP0U75@ART#Yu#WSQL*(waZSR zL{({wT};7TQ&c7+{c1N7VHg0013_ShXNR|khA{#G9KgRGS|YsiXn_QQcwIFj5*m9F z2~FP+8yEDH!41p({=HhzX6~tFB6jmMH7@IaLHIU_O1_Wf|8G`QKFyRwKLSWeauSpY zS|92_b#xBj8Qiap#70a$yLQ{VLz~gy`zLwDqh5s$1Na`CVve^HsUOP&C^v2;Isjt zQU#>~6!z#Gi5{)G=p6a7>DonhFAH0wX#DV&7I7!053qsX|Nfox0&{F-KTp>xN8e&((p?z_Be|$|$x%B_*Bi^PX(Yk*YEb>B|tVI}Wr_4Wu zYe&ee1ELWmpuIf-px(Eja`4gP`#DV};0kHu(V%)&S!ZJ$6gT@vxEg=0rl4XJ{Lu$4 z%!sIz;QG00Jo><7$S{fSMtp<%3^I7~7NxHx<$L<3;x_Gi-R2z8zS#cT$NS$l-tfu) zM-=Ar4z2d9w8j$--n+RFcw}=0>LakcDm@ZKIS8Aav3 zSO6$GI&X_g6%v4MkSEyH|E=PqzG2oEj!|a7Poiz=M8*{$&Dm7&Z=yudlS0VIVV)Ob z|8%(E#XrG95DMrz_6L{FXdLkWGyWY)ltcTn)?=L9-Dy+XHf-3k=bMr+V^{F^Q3)~5 zht-6}LBgglezxOfTjq&o<2tx6hC3uq{X>TLjSn;Of%#V=+iZRWWGc>tC}e+KzQqeA zgV@=a?F8sT5vXt;g?i9ROE50x2~cf-7E$E{{5+^7DVUQWytTg7jdcOTU*RYQ#Jqoz&IZo$Pc&0{`-mT6LPk1_P$|#cH)WQrnfV9&;xNaPiRCwa{I5> z|Hm1C3mV-cI3G1BiVgq@gWz~r5{sf;Df{3%JedC(B#zQTfESSfpH$02 zJOKHcdnGIfzrxpXEb%4*I9KrCpRXH?2JHQ6`Zz26R}qW&+eG0e{9m7%d|_{l$6)l5 z!>Smvi0Fl9WhB}@Rd7w+Xn&Y80j`)7QV#wjx#yeYr<+>zOAz(4)WuBPtjF z$~pHR!;|{bzJPw$mW}^8l>$ln$5{CVu^Y}*=Mx$^I24E`s!@1PXZSq=atvp5%B*`% ze}H1}$DE0P7eb>=J&Ba>+y74udiMTl1o=1PFz~ZMv4;TKeXZjFiVzUqs3gAa5*g>w zq6-bRw97@XK$R3Pn}%BVfui_AgzNRbBWygJ#|p2hbrUk4y-(s9ieAB zf1QamFH0S8Pv18+ftK)bFcW^EgTJ!}uA%z;>D2#qpl;=ZwKM z%E6peRPM1L1lac@&fm`w2UjKoT$!3Wpolm>Y68V3lZ-9soDE62RwB~D+#+>XsaqR9vwM~a(kIp)-gBL!mY@Rb^cXg{3$rBaFf4lT3$PwYM( zrX#A_d$MU(Ba59V6prQghp2dUL(rLyJkX>!mF0w7-Z^b{Nkn!eLCMH)Ek4l1@}gnFQvQ3U3t~Ii&f%7 z$S-0nT|%6(tMfdcVJb`at0C(+Msrfh3ds6r8WHlewxxJ5mgI-5KZ6oJ zic2yqrlTv7JMk)B!u;ia`^lW<(Rf@DuS1Et3qn=z8puc5*W@qT5QgHC6xuE;@(qNH zquF2u3`Kfz|1Q}1o0*=O4U~k)ocx#E#h)a!*TIvDVehD=<6%Pe1t0>58aKott`(2{ zXtpihA;zhTCQOn1M8<2sLi>rBE}fhxITeg+m6*m?{ugg~Y(5KrfHS7z9%;2u|8#6~ z>>-5c@~(0}{6*%eOv@|zJO?|flfY@2iiD+{7$9<+4E$HK*GA46G&z*(5|sE7}yD!Ah!hx`4tNwiN{B_JgxAN*!fUm-Yx;6I@Z|JqD#4N zJO6HUp8z>)1yQGz5T(szeP|o^?pS|*$5_wZ?N5T;8!23a2BS>VLS(Vo9Ig36QyRa_ z^3Q19C(TX0S7?jPmU>Bn1tjKNs-f43TC!XMqAzaKhO^$^S}`7(^DmF~_&0IOM?NaK z?|MC<<~2tXT<@*BTUV!28Y^#vV+_fWXvXM}!Kerho0Kqa1>C&RxE`y72;+XlqWJ#$ zZIDWSdQoh06&1>eFqO4$jvH}a;3~w>AIn9t=z2V*cfI)$MTMXbfqNHv*J6aHw0!tB z5;aN)T~=dyw^?-~I^yfMtU8<$R1QWrDu7K`n@fiNq52-L-Oxt|jKi=ql4-3tcPSNk7#i;_dvOr2eQ;vX`$ zFH$>1z;&IhV8pJHvr>D!dwDT+u%?4m-r>=JOC452o@o7ZWjit0%~ccH)<@VJ-4~6; zv9{Qj{AtO(NK-Vody_+8-C6iwN$rv@Tqr!nGgw{jw>{Z=TKIF2%#=6<$GQtc{|xn# zOGmhuutK9bvCVjrRYGtV!LyfTT@?Sr2A|DeK}*~Yec_Yz`3W9qj5#NaOSy8BbZ0rW zlTQOV+j7&O6QLWF@l$ih6SXn1nj7w(uk2TrJzv9qG{N|Febw_!}4J}`A)(@yF_&rrhlNn(_^A_{6b;dlDMq4d+i9TFcwz&g(kXt3`2r zcn|t#a^bcFPsKzt4?2dxP@-u^X}`a!|3m>x41ihMfpSv`j`lxzbPOI6c~q&-TzWdn ziVk!%ZdB4c{^N9XU5+%KF`mX?VZ28$%z+#x496N=73_U4 zS1ZegQufYd_ThbbKD0+Z{RDBrDvrsar44D<#NJ(+sDaWh?g@uH4BokCwT=)mmE%Jz zUgg<{5%Y8 z8|1b$8KQ$5`?Ly{*|h1zaRrqKK9pC~G&dNx?bLb(f2NYOcUq1peC1*QV22}beka*! zmLY@_N!(ZXG(Ht{seQ?H$37X-qJ`BW#2&M>KPccG~j_cqSdYB*3tZO*QvHuyH!q>p?hd&^tZE)(F@KG#>cIS)y=IxICMl#zq zPNHT`^aXmjnq9uJ^O;Gw>>xqrlw$RSDz?XFpYxhD=|9rs{UW=&&mfh9 zcsGEBIuQ$}aXl7n^fZ3y5@xK0aPLsH_taQ)vwdi$Yf;ysxG0Z3LdP)zmCcRP4I%q~ z%n}Ho$jc^ispBAcY&&QK+@WClxx(RcD&E1ydTS@0#4$LA9sZJ+BRc8@t+r z|Lo1m4BEYhebVaeNN*Mhz>cHR5o@!qsW{ zX=UE3Q@IX@`$dcM0<7y-`3kV})&cCm<@#>pdVV9;FkFD{K`uAuN`3NNo!O+8L4vJG zv+r2w-B}Knn6Aw)x$W%#yR2C8v!X|opQs*tW0r8sw>w^~-2_|ucHjF~fB8}O+9ac! z9LU~AXp3au2<%Mgddb@g^D{F!3u^T9S-yx~Y_QbSe6h%p!!VtSj6 z7Z{XhtE#hl>OQBLA*otzz}Rt?`K>TN4Qge?sfR;WN$Q)!1bg4=vb*S7ZF6g~Gyacb zIheh?GdaammFFk6Bf~}atr5xyjMBxXH1Khi_W?H|Yn$Dq|D*!tAtG$RO52YvV2`LG zHr6q+s3}E<(_f)gR1RV%glL&yxW|_F@}~YH0+s+v!XZo*3H0+g3EhhDF*scu*ou$N z1(nXnO9wtJrR0u18AdFN^SGP1UhBjaaFzFgt8C8}>niwj)`CpKggD8*k|Ly3*;SEW zDLBYLGodsGeFuHVTav_H$>~BMVj)tsO%rl(W@0}V#tzuQ7cU?z%(+I~3*3ubQ|=}1 ztOe2Vvh;K;==77$gMz&2ADx8^7Fm?V36p9+DGN{1LxV4o8#XvT_{vHq=$1{_w+0SpLE`r5d&C~jO4k`LID{DXA*EA;J$>4C888vFHbcaA zmM#PrNq&8HPxyE~LjvWN(}S0omdS%5rcf(N8jt((k&30`Px=JvloO0E;xp347FbFe zQ-x-kXH2VrJEDu_>1qMH2XKN|7TN)-v{0QqsW^EWsuZvj<;G!|2sbA0{Ktv$Se~=* zFUcvhAE4F+z(av*%w-4jR>Lv7M@Z6@DFih3CZxg>aw(76N}ePW$ajsyV8{CcfrACu ziQq9fGpYh|^lhRxmd<=&i}A!5m+uCP?+mM@aAVN;d*3;B2ZMduxIPxa)3dz3sK``$=)sH`E5-mBp zjC=cNY5AA7ITNM*O4Rr&;%u>`W!(h3y5xenjEf9%N>R5lSRls;W|_9Zng|iFs$N=r zpw799k$3_OTG6y-nDp9^$siSGnVUnfhc709KQ^uynOa1&?96fH1hN(4!IA5(zys1FLZWOsCmSRPcN|z&d-%X z)}IJKq82J@;>aGYxsjl_SlhV= zWQZ!2(>PzJ=va?uWtH-;6=lbvhxr-LhIUU&5Dm-yj!+4np4sQ+=!2Y&2}SH!5bOc? zh^JWI7iqip+OKE*e?+wYBsD$q9e`aG>rcVk;la7r6ggPGR@i*gh7}AiyB{>8&fN33AP;zrf;9*E($R@HL6=b>j{j*G~o1jNP??5mj~A0O2!5|x2at^ zX7lWC+4BM?nre@qeVFWOPGt9OH!&^ThlDE`Cj`G$RhZETvI|^Y<)DU(Ywpd?CR2x2 zvBek(VzBV_H033Sk7-AN8C8n6`kMO+1&xRGH@i5=M5-FoP}+k9elg>+BaKtc7ArF1 zs0FhopP@o+)If?0bO+kn!(m*-f}(9DunPk&RmM%eGHQ|ZpU z5#@L8JH;WD;_On(r9lP9S|Bgr%-ll7yKsmK_dPCCd!b1qIOO@x%a;eOJvPG94YzE_ zP5vg=2;y~bV0F@xP~2yF!M|03XLt;2_~TijBT+MzPadI%{m<@Dp#~d;w^9gQ`38%V zcw_zI3pDK*&Ry5r&I!vmvBor2tA4}d;Z{H(*R`EHPZosoXTV}D9hc)Y9n1dk!=)UX zeo86WAe2!AOF}mYE)Q}QIBaocW=0psGaXUE+z)30pK z6GBqbOMOp84pQp!FvBE1k{=N(}41k!#T#o$X;8l{X6 z;%==JCG9V?PrYRayW+`oOP|Qq9Pvvd`7vjxq34nCOD?>xOY5=o@(~#RiIyV~k)4F2 zoJ01Z*()&O@5Wj6Dom!%>-?hahJFXkxd+4^kmnY zUzTyN=%1Gd8=tmFhwLaPdeZ*_dlQt!HdJbM~ z+X)W^fiPGjY|TQw{gHj<0j9zm5eo{4|L>gc{dK!Sdz0t94Vae}T8{+mfQ0gVz^r+2 z4>j1bpLIT(07W>BI}}VA290i238kdlwHa(13#^q|Z6u)0T{4+d#rdq;xbRY>jtj2Q ztoSnh@n>(Sqfk0Do7*``s;f8$P<;kiFtE4e<~j%n(!@T`n5DI3VC5`$hBQH>ZWC7+ zm3uN*Wn*Q)z6}TS^Mu5&dbh^k-=SPr7^`Ay=3n+ayyLYL&ZUHLO?=%4idv@3T=+Sa zB1Gq>Wui}!!I~O7tn{=yv-llqktlKx@~$Lno4L{~K{4JQr6Tv!(QBvP7hlf1XLlhF znL4@r;(&bF-{;$xM?5`-SDf^l6s8Pwp!;Xq#0n3_fX7NmYHYJ}XMC}>n0 z3r63i$KO?nzz!Hi6oWrI%PNL|gsg63r?>l>_@GZg0t`Xb)g_?&5O21@5M%+h_J}8~ z81?`MZB!jy9pFC;8k=5h?xXIZ?YiEuzGEmq*sWioNt?V})suNWyqndeReT7n-CTKq znf0jGQ{cjtG&;(r@Ttf$Y`gzV2Cn#^;yZgPXAjcpFl{?>kEdQ<;KmJoz5sbVy>Tcc zYyQ!+7pd6gO52XcG&uwR0p#xVeOn_P4CuL~%n`>IfDZ1f``liOUo3!=x;=B|@e>bo zYWN>vW#}}5XMd}7-UOWXgj+H-Z+vk14X81RtU=Lo*JfwGgZyi_>^J}}=8M<~<|SAh zKVz>5y~KP{oJ9Hd6S^i8=SG7XQ1=_xHWq1P-(#F zonxzGi(pxlr-46xGDTvVb$#dM1Q+1Zfl6MX9zoK`1Sh+l&)+tirC=0HzuI1XzBBqN zsnO{qU-k4kTn}1W%RIgKkb&K zvahKJmc5oEhFI!fB<{9d-DZKIh*@Oh`~j+J@4UE8+WSH3OAuPpN-J&0!VWt2pPyC7 zF|jJLrKH(1cG88$Vpft=^Z}jQJ5&Vthty|7pVOFR7yIdtY#6!L-NhBP z&|5gqGs{OZ2C3!9RPa_us!@HbLa)t3VONShJeBb57i;K+%%; zi8iCVHCcELgMp3TD!0CTE1X`(Nk(*3)~!VoQ~4_)&+`XKV1Tw%8nl^+iv~Ug_-hS#~dEB zIR-ra6<%ZqqLGwgzxLTPaBR2BDOWzL>787C)veH3&%jXwwG7rF0VNG`*N0-$(yh{l z1znb(f3oPMi0yUTeyy`lLBNpQy`mHt;VHHY*XXYlxDyq92z+rcC>Z0)N75u%hC}^U z>YFbBwKdX?UfX=&JR-b1h%&D?)O)ZRiymGqgfqy1Td>IB))f2504!#k$G+N)cmWT9 zd48@9o?8$c{2Are3!8NI#FF|s@wKjbOA~8wROhJo^@jC5ZFfUWweh22iOAU>X@j>` zi^3|-eNyzZ&a(;1M{D<&P90H$sLANsjfwC4VZ)R@P?B0t z#_3xcz%2ro{Lok0#Guh&g;MC?;^b4cyH^dCe_asQk86Kn(B^fpKF-@kmvF#DA0-ue z;u?V^@IL}@s`9OE1}}i}fXjw^#xWG%S)tsxxS5DPY5_8}(C1A;aASS~Sgcx)^@SuW zVfJ_~%UD5|k1@S1z*i>e2wp+3+EE@Gx?ETRdKF0_w}fove!`~K^-A(-H?*d6LJsI! zdIP(1Ci@n16ZtJUksM8)A%~G42cs=!+sZLutP+)%Ga=n8 z<5rnjSin31V?SR0Ov&p*6I!EXV^zE?C=umquxN?PKd~Gp{Ga~JTH&j91IjO*PwWLm z4i4=n40#+S?8Y-w!t;@tDQrCz;8O)aeRY~bD(VAiL>jESzIL#!{RR8#7-Ooq3L6Ab z$JS^pw^^lA@aQymkHWY`a~$iGBf4c&HdkdmFi~%Psfh74W=qYmTRQECV=b(7Fz58-Sfv`PyEojk6HUwPpuL_b z;M3ujYpbMp{zj$=at4)y<)m~?Y`p{V@j2_FKDm?>j5~rfxsa7*ObEeEv@Fh6eJIa@ZJ^6R;EN5pJUIasm=?6Oe6V z+Vt}=zXo{!=li%8tNzB=JoXC$45QIj#@yf8ps;qTzwNBbdaNqWYe)c_tK6#Wov!NS4I7i0;*HuM7tqq zZ4cm33D2ixglnGED}+}^Q^?~Q6lhAYpo%qUjXP7{#7A&qCc;d?S+KAzzcwiHbVpf8r;*+OiG_XvJ4{cJ3 z`WaURNjmGaKJUo;zjg7`P~^|H&CDfLNu*z|rwIl9S)pv*ncA&U+UG7c79p97Hn;Pi z-<&h~PfU>*61s8Pa5X~YVCMmo80Y$nai2a~JlnJ(w_3!D|I+?^`jfz~%>CgRV<2ZY z))*jq8OGs`*y+Gqx(P7Gt<_kyk6B*Vu}BZdmx$GxJZ`ir#%Iq$<7cGt3K%Kh%CIVsXUR z-%&D8&fqY;X>we;yF7l)T%~o9Pr?d4ct9N0FK%h>trNPC z6DesMy?yPulHInK)+Ygb!Y3=~^;Oi~Y+8v@GVNbc3O`_x%~E;MZP1yIKZ?b!Ng(Tk zd-GciP;M1@af{I!%XE-v%tv??FrXj*fo`CfN!P5&qv?v9|FP)Wmq;ZqNb^vshz5?C-AzxK^?LfA=QI4GZA3aSgqa{7J?)DxC zqltS>rq`EHG=G{2BUldR+&G%Y7*?o|kLTR1K&G11bNVzJWk)~}d|B;>(`ID5Ha~TD zBlJHXQ|pO8U_(=lp||lH6f0-GF(V%uJ~kMQqdZ=EfzHR~?M0R5{AJb6U$U9pj}vt2 z*KoMV{P_Eh>!#lxD{?~rlAJdCC0*iqN2i}?)NIe_?q>4R8r>-hz%vE_f<~TX2>*;&Yy}xBuciKw1I|-4D7=M#!`-6#nU$tx@ zvdS$NbKrFa{xLq0KR&>j$mdLeu}L7u!6A_`>@B=iMEPp;(rgeI4gFW|OM^w5oMkbF zj$^^AFzlGfbmm;do4XSosY4$cunY7r@m(`cmcZ?8z*=?kW;?CNxhRHPvgC-CxSZUp2Q0Z_A zuEiG1>Bk1Wu72WM`mm4_q{6;E;qiD>wAmQ)3y6ns;B|0VV!W7T_S-KtytNt%7pV^x z^`FziH{s0Ii?bJd)>?WeSFqaS_r9J+a;geI%0X*F&{j5`N@Pj9isb6?&=Q*fmZ zJJz3Plp~DNQ?5iVBdUJgup#>`m@M0y;@`GUr{`2XL{eA--s{*a2g_2_#%r&>mF>Ce zx|*!ZejPN#oY50Poq-MIVQ@F<%!IV9JxvsU8l+`V2i<&-ar<09!fH7DhVakVh`R$0 zqN3x{*LKV>hpc7|7Hn{lZ82nTDxNaGzjLs8c-(3nWSPer{6f|XZL|I@p}^^?+$)Nc6uI*v7mL_Z!B75)_?)B4KUMfH!+KHAfm_-) z;?G2**00vQ!ax0gU=9IE*Ig2L^QW(T=Tl(3XBfntgzDIkeL;U=~X3-%EpeURY$k&mmi`v9&Ps&Q^Of51&qZ7ABb|cxeEN; zRNbz=it65pC#Fr@mtnWS;l!cy85HW3C3gOpI|z_EmtrHpHYSR@4U`AH^6(<)VUJRM z6dX4kjv=bLTNmyOu$*>%&uwRz09Q~l(DPpaR5ix9LWj>|4IZa7gn#`d=`zsfj%3+l zf7rWwy<~gR`>x=}YKo0oNs~#ZO|azCiN*!Zz=dDBN+^RtC^to#ZT6JCS3n90NHDx8 zBqAfFF9^YUzU>y30L8hR;*i;TT?u$kf_dfP->sIDyZH3HB2i^Qx`PAd~gFeFc`6Qi5k@cy#lwUyPEKrp?KU20i;t&*<^FcM0-6WLhceNHi_X{=n ztn&I?qs&Q{hZQIqdtwg(q*Kmc*~07r!z0UwM5EA2`M4Gq!@7VF{Yi`s^32Y1r4$JQ z@|IbiQ<$d#G*1-aeFWdEtbM(NZ&TTM=|7|GTcQ81e z3zqm17JUn)J{Z6La34ezB(mec8vjE}Xk4&??X2c5<1oj=7soyBqS1N^421DU^_IPW z}O8ow%1uAX>~*Mdh_spLg1PNi>8O#(zY0M9Kd5;6qjAC zB*HbG9brUDf?lNRBeIEYq~TpuG~rx@Nqz5dGY&Y9w0xI!mlpdy7Er4EYY_+Gb7nwA zX|o!`YUawWN6Jq|r==~mo)`$SxcUWqD~-bzEpYfEdO(ftjb+M9%8;$C!tF3=?Wu`48>`IghU_WlAm=L&wI5%JS;XA- zAHOJEd~!9(Z{IwVjE6p8;s9ajCQf30lyaL8PfPtRkC0@;Dpl6CHsCEm4RxCDQNP_e^&-G35&bv+TOP`0XCm( zz(~D+)Ex$h_|TYI+IG}RzV63xwX|~CP$>Vei-$xR$oon7I}{?(?vGr+he8hjVfX8m zqNzWv(%pTKgCJ-eM-z^*|afD&B;O6_~{#|`RnFH zBLBz^m=&8$qn{}f2#(pgB;-K)?b5OomtKEeX2^Gin%`UC%TiTWoFbmMMzAe|m zhkv9)|KiZ%#tkQ*iGc5eGoRB??OVII#h=NK+ZKH({&2JuBWfqaPe?Txe3{KoOzHRf+T#QbtkKFq$Kl8eq~ z4Y)J`pc^!rBsVjq$jSivzoYQf2)WLO)f0w%H@}!rd{gMH6nF-Smcz=IqmQU#w?cxJ z)vlENvty$DON(LnL$F$*4<*p%&BHhoVEYCEt>y+%y`h3A_bB%&hj)~QSq~E$3niII zcceGiele*$AkGOdj9waOdj&@RXP2Gt=VUom+>qhn`#+rFU9Gqpz4$Q$=09G?VIrWi zHKJbhrK|Z8QxV<~Fj)&nY8gH6@QdtOMWvS!bQ7KBgJ3!Ij&BbDUJg#;zG)>VAuLPf7JCe>nYUk zmlGjQe_j?&KbV<|i*7$YU~WYSY6;COh+Z7^pp`c*Z4IObRlh^b=2s;VXVRt^#Zwo` zG`@mjICv5u1jl^JQbxL_Hgw$Nr=9p|!&!#NOR^<&)W35R-KK4m=WF!~cK$gsnm#R| zNrpn+Rhhe=m%TF8K<yX!8s^*kZ*0p**RUs_qjc zIu9K+7-&>nYvx2sLDbv2F3s}aakVB7)$-CUhd7&J_y23=pxOk?XDSVV;RwYRZ|jE~ zFmMo?G&CWgB}%fTYJ@}_|Cn+&n5LJ&nN{Xs3>B4`W|iYd5b``?zZ>W^fzet}!A5{d zZ=maVUs7O~R)KwQ(uXs(cC}u$94(gNK#DC!661LSYH%f59Cm*^ygbCQY9JSLdL@h( z5hQ7F1Do(a+`PrCCS>oh@2zsTm?c%-s`;ema^v+zT|W=pSu5vAI%t7?NDOT~5IPs* zH52b@Eo{&0uPBZt@|+8bE#6`^sk)02N;}#oiZ8#v=XzIE#s;ZZidKp$CYBzgpqvre zSZR>u0qmJ#URHKEP7Rq@)sy@hX5jrg2~nTF>h?6ly524dByyD7XHPp1dIQ=O5=z_t z6)+p}lom5oyR2Xg@}6o~x4{q8lJ)t>G&`8Ynwg7@1{#7}%;JDJGNks?zCiBLwld0y z7WjMa23=kP#C%USq(j*&KSf*p#AF?w@4V3K6l|SNaMe?%l#7Nc?&ZA^;huN?ZPWDC z{1LYb!$Zg1g`gk%`ZU-lV&3bnIL znm{CA5j1aqN>PNG%Dcx62vQxnq4F8QwR`2|CAtU8X-V)?ExDl?r)W{X8$=BbaXbvT zShz)<24;aQN@J=W9{mtKOt}~QIX}Yrd1UYKqL~muQSLxs39jRYZzENjZ_2-K3A>@whgWR6k^|At9woLT^rQZYr|X8q|BGsF(mWij>%=vB}-H&KB?E$xPq zz^WH=pfa=kMme(9jo3k!=#reNm1GCwf(Zpzti+gL*Uapy7%&*449xE59BoSpT-h8J zpb}tFaoVcFwgOra;n^k0P=TAtKzvE_t|<~qonm1xHivQwf}asxb0-yc}&;f2kQ8RiWmw{KkplSE*^^8sUb4Dodlh>2l z)u!{&2`-!68ZZ?EDF^SU*w#C+%17+)oIQ57!sK6TAx~=-7yg<=~ z>HGOWkVa{A4(O=t$!ITPV`Y&LbZ}N0>QZ3Dl_dd~Y`i4W0nQx&4X;m&-Mvo+4L{z-aUpj-P*<3+7?7Xh0pQOmP630#E>(gtrif?wlm6YXUe+# z2N@hvl4ZBs=UkD@g~<)YG+G^TtKC>GFOf115`H zKO5O4n$F3>l%`5BN9Jmi+0Gy%VrnY1`oa1b=e%)pio}csp4WaU#QL%**qiK362^Op z?p?>j0#tR2mHUzq3I752u#Q_zSx1r2-kygiVfL=jIS5&tEZy5IAzwn-OLD3Q412x$ zA1JNM@jB%}_bz5c+kwg%zu$|jP|hlrWpdyhHl{$jNBYnm|HcnzOq2%#7>XCa>mC4kNK@NiHK}C@)$R4ABpl32-2fo}*#jlA0D8kMGDf_HtlsT1)<}?+WUbT~a-e@X4Wp++} zvuFfKyD}_7#^%?4 zDJuR_X=BlXN47mp`*CnX10@zPCQVkBF|Qnn9W3Hqeh#k|HD6MV5q?f0Zs}r{9$r2V z-U;?}Q8t9AN6aZsW*J8sb~YYj{$1>Ye(bX?qNxY6{cO&nv|iNk(l4!O=e_^QLmrO-RJx?rt+)lNC`w0FE+Giye!rN_u&gnSF(zk=A(u zUzOA`I?rEeqqH}Ae&7P#DPm73D|t>o@8rnN4JUv8$|X*HKF#k|n*3vgRN{WGl(WiR z#7i=wg0SIX?}{7m_$jvSp;q5XZ(sCIU{KF^qH6~`!Y7A%4cfaLtnInrr?e$^;VD0g!&O=={{!tZe+1Au%3;m4K}G9 zH2?1k6G!sz3C3ZH$d%W+$VEj&vg$x4`{N(A3xHXq%A(9vIu~KyH5SsJJas?4k4z_i z&WyA*07Nvc@~RMwkND6e^CXp;FAr11bU?{9UQx1neWmD2zK_32N)fkE$#Ld{R7XCL zHXnn&uFGFQlYrKfj9t|L?mHN7DDFu;fnyF4x(4uQ^Sr_ev;`z@!Z#vy*>Qd~YJ_fe znTDsBO)<`g8%QCYK*6o6k$bnBy1d4}ueyFsoa`RO$S`=|EcQ_L-0}t6*oxn;o(Ey& zqZ}uEOL?b_gN<~!h=89w1(=SEGYi1@n385_uf>r;4m%!QIQ648MUkpI#%?~Y8LFC{ zzYNyhlRt=>eVwl-KFUj}xA7NrW)>q4as<>x_tqV@F^l9{jwHfWpLAduGA3jGJTxbT zVOCt>cEBPXk<=d4a>o5h>tW+w7;g7R?tqfsp`WYbBeNQsIp#Z9qatQ?2IZQn3lu>X zW@10^aVl^c-}%Q15RFsnZnV9FISv6QSl(I+p4|3?#ZEzGRBgOqzLb*`*^tKJnl(kX z5Oj*l534hx;M@CNucNBJwHf-YJPLEWZEOGloZ0;h38vW_$2y0(F*0v9GQI-i4N-N2 zbavjh{!y{zH^eYD;w>u61JW5C?Bqamv^G^Nn~qf)5Dzd@ALS6ilGvpax<#0LMaFMs zQyn0>$gk`w(Q07DP-kR7xhz3;tTS{Vr9XLb*2F$br!FiLh<( zk?8xilWdXUZb_T%aIBRhLLqniM}HG;_f>aE8J06pXYO;4L;Y>6$<9kn{N_A(+QC;E zD0^|m=!L~_ypmL3w2V>9&y^tDJo~Bg{RE5cl3Eglg>j8=BfChg2x3qp3g-?7y<#QD zU*!SwoMNZeey)OG4T0}VWEeFcL0epHBNeAqVQW$CGgysf8bk5+WtJY0Y6;M_=Gi&L zqA_DvMJgv~$!`ZFD94^U+s3ZVQp`r-rF|}~)8mCFhd&83_CW;i)1oSa5^PU`OJ|qG zP@ws?gx;zuSMCi=LMoyr;hKCdlTE;-U4C{+G1$mvgg>w*k&Urs?_UG2uFKZUgnIJQ z3V7!;ogHvZ7iMeu64_^WY&csca=>lz9Q9u~zg$WZ1dT8eByTB3t@;D-ceL&R$PH!@H6@A=ht zhD}v)JmyUx_$M^I-+?9qpaQh?m>4DNPF3 zN7u`(`kWDYC5`GB95@q@s+`fp6+UUB+hV zIA(c6itI^)VoMI^L_16VQB|Q>`sx*YT6f5Y0^P}X7HG^6rp=@qPv;faF3zV888tkU z1CLV}2fAo)9Tnz}ENy$?VkJ2!AG0+6dhtDYa>*9AlKYbg`P*%{Lb(vxbnomQnkB2w z8O)Q-%(}81D$eG|R#~?(m6>}TWzB$r88^|~AjOx6;QM0n`TRm68P6K1;+D6w5foq; zvXH@|E%Na7K9PvFX8`6B9+4S|`B-~2x^EsAZeKv}m+Q~}y;Lu>&pzO^HB$2FanO11AULg&oC7;C>AvB8E*c5S2)T4Z#6j>v zwx@;5qGF!e8Z!DfB!{Htk0jMeZP=jqRdfAmB5ts0*|YlP!l-MpfnQ`J=R<=Q7S*!f zzDLW(P>0n0dhI4Y!be)T&k>l+ce%secel!%<#mu=%A$hr-}@(*{FE@v$h)1F;|Ov_ z97r5yKV$;1oy%XbV`h`XPbh^YeW;0tb~%9Q?F?W-@j%!h5awdjMNn%8MdiPTYzq|I zp1XJHesm7#-In6~*QFHaBZqS9!q9VG-AK%2}$OSlx)rES{11R1l+VfP{*c zYWHENQthdvY=DZUTv`a{%r7?~7@ymK>!cU<5ieKT;L>VdG8f(GH3(p6A?*BYZFL1? za(orclXCPr>YD}`ILy!J~#r?yuzxbUAj!K#Q5|@F4RF zPct4JEShwF;arBc(#r$&-EG{drgt==ukYH#28y%FJoX(iCl(GPPVX zHTS8;Huqg~OUWe_88b8&pqWx9bE(ucH!Sy+)I?Fx($aFxL{Sl)QXxSkML|X2eXw(8 zs_R_8@AP{OJ|4bXLT{V0PA2Oct=daX& z)S8@aq2NjlQNH{G%4cfH>mJ4`oQhiT(7m`Yst{S(?pBK-1nvavR+Uyc8rn-w;dIQ= zi@R4ArV3vLZt^ex;<>}x{gRIHjh3Vk-a+nMM&%zibtn2PaA~)3!8NQsLmr-MX?iOK z79{RBKFgoIw&C1~$=aS|h&*t77qin7WC`yHgH_Q1q%tQ%pRlHRUD}i#&W>_tzPR%6 z%i4&HAwQ78*}%%L{G$}MDbMep66I5FvsyN`(L?#Wm`m})XPgGX>>oxb95)_pg02@d z+!ZWu4-5&62-FUQ1m4$vi9(z4ZGi5M_J!9d7Cy6dY%RAhAR7$X2fCVV`KH)e&Y5|! ze1t9b{MyKZ@WiW?@cM#R)}tE0=clRlcDWf@4Kw$C5xQjQAj;vHc14E$e-7Xx-?|~k zLuutwpaXI03Mj90U&!CxqqlqQbvBM_$5>%L;Xep37dRFli`is)rN7taZ0F8f=L!_x zlBc%pU-K8F88;Wr8TYEJ-CbsVe9c?KY1=$|H?VpPf8G#so^pJt z5-X98YdXtQQK+V=P>^YhsiK%xnO61HwS{;}9K=GvR`PiPY&vkp`0f4<4vz!!_kk?^ zy1+#rcP!Hmrg(2|@|t(=j1s61@S*DNhAHL+ex6d>M7rV!_cC4?W)GgPMvT_hZ;5UB zNj&yr-wqh{GUaB=a^6GK^8EmV-}gT~UkUJ`>xK*R{}Zx(johk9?=(yISmMM@gK28E^aYikIPc*Wwhnr2H=bPhroG?>meY z6P3>}=kF|ug+bB%5g7k_76c(^SH)V;230L?gVMp_o)p}zss>yee&@HKhBVu*_b7QU zc|ZAQ@*(na@3?mIE--%S?SsY6h$@Zb_l7|4KmM)v78Eb=;3>MO$2Rm??Y`(aEwpBM zZ-mRyZnLI2{)%a2?QBS|Xw0*FhHq4vi?wWm^+(^!1@+V_PxpUc>7Q4%++mUOZCp)= z=?>IWPC?qyxgTy{iCYZ0==e0M|JEDr)bryACp;MyKc;SC%6Dp-1C^vEiYp8Qu>YyCD^GzAyGB7Iiq>V zfari*1RTiQ3McuDCB+#}GW`FUimU=1=9#iEOROwM&rA8Lj)Bo?^$TOJDlvm z(D+5Qwbh9p*$#n+5fDSl`>24HLDQ_uigtflr#g=9ov$xf6^4M4U|Tn@TR1E_+B5Ba z(%oaMH*a4U_1#~(czBQUx3_27%8)ywa*vysN`U?&wMbCl04k!dxL^m4A)RsOio5Y{ z6aM_wjy~HdLWGIB&uCsD#w0z7+gN&kSSNKxD?CVqkOKqIHL*@2!=HGgcWj&{0y?-W z%6v|Sl4>;%;@|Xz89qMwh);U`#NxN|OAX%=8$mii)k{7~uzQf}-Oz9wW~x-HSt`BZ z#>RD53(D_Myf1xVP{N z*(RA-!;hyP?I)U{79Z=;*XZ4^T}?_A>Xn;<$3^>z`%O2Q`hUMgld{tyNb^*X&7WvP zbV~+Do7#7A{+#*LXjbFIzN@`8yvAAW^qKq`P+Ljy_Be1w$SnLIeB7#-6K33x~2@(>!KX3Yw${1ibz6>fezXiJxDV>xRs)@S!TnHJ^y(xRxV}GZ>=Z@#?tD`$O(=bP1;BE z5lR_};g#+oW2X!rTu z(HCJRv8NB)UZ+pRoUa6f%5RAMQz0k}!n$9Dv3O)-<&jrnf+5!6}Xn0bWdhq+J zffqqP=Dk5I0I=!6gBe{c@H;xzgbQ~CsDJ0~8(u2dxB)S+C4bao@4qgs`LkukUiz#1dm&3! z@a3Wx*o>*+L#%}~lU}E6`j$n~d&2l-*UA%CuAAGW#W$L*pqHe|4Xgi)kc0wN)3Cj?Z>TIl~UW&AQa%R6uTO z2Q7}wx(83+nXC^Fy8OdubIzK2*cwU}nObvdMl(EG(piyDRU;EatLgC2@#|g7*Sm56 zNkNVJ*nydTi<`iJuJ|{O-;i+MHI{vzz}=7?M%x+sV%Bf)bq#`os)EG1D2<-2l)z~F zGFVm*l|3VUmhRlfz%OUalH|i$RV)rHqgmH?Zo9=O{L-L=OZ5bJkB}Z%Y_eQ`DMM< z?bY02B5H7ph@Ys>pKHvg#V&|X@xKy;d@ons2#@q0vF~e z9uUM;zo^3xZdL9cI5ZS*2G_fOO?qqnPwl^B__=x^69AI`@osO5z0cz;laU_NW;Ke& zisXhHv10Q(YCLyh>6SxJF`Cn`I*ajBHBcL->R$tQITG?5|9jfP!tB+KF-_>7&RH%g z@aS&y)p|#`8Q7@Jdzp4BD*@}|&iP#rEn__w@{)M%;o#67b z&nAsgEP#YniDZt=5;g+?TD=J_r~0YG;*`;^lwBXnfb)L?**W8tfnQ&60tsKZ$ty|m z@4EY#K(#IR%HE+luLGA(=m|!32VI+l`U>O}bTP}_JA4xZbM5bN7QliBvtZ-vPk-sF zyfy!t33uR&_X8oHfl2^>PQ&C2Pqrp$PHgPaNYs^u_Q@fAUi9a&UqCzfOnD% zS2#X#;@xvNHi#=&lk^=iqh(OBO~~tZ{5WMl^emMZ`E4gcG8mX zi&`J zetA$B2k}Y!RA>V#J0xIy{+#p3k*nkR`41X@!|5$7sIJc)esDuDO8V9rnGs7TY0s2|`o8xo zeY5cGvsWPFs+~=Fzu&HRxU+}~Bd>iQi4nA)^lhapXa&0Kc2rz_30;>yK06!TbSGH< z)se90v;AorI?Y@%?S&TCVSSdnq`0B!X$WYyPAtv=VW>6-gj2TjsrEuN>nI*W0yB@ zvcsF{7$&{-k@SZvJA!mq-v@;bTVOD7*N}JV+m7AI6?Lk}P&`Fip>OBq#IE|^sD2cL zU(>H!GC8UA<@u*=PMB==LzAEF^OP>TJz^<6WkKpE3e<-+JLssuB+3#ZKoMx;CHep3 zIG|3v8k3Q@)gbs+V3&npp~f!?EaWN2p#j|Yja9&zT?5`t#&>;F5IWA>Wa>NGt+S|y zJEviOEFhx?FgmnUzXW`V*h;1zeG6FbUmrSXs5Q>~Qu`5~oHWW>jq14_r&h#O1PT+- zF19|C4U?%m$j9j*4F%S5pC!>(=B+Ppz2}rim*)@mrH}H8!`zOsVig}+<~aXY70#MUKKWW4YDNP^WRYXI3}=fLUO zy4Uw`vF{F`7jBIB4j;iLi8$M#n7f#wu|JGxisq!}|a3;V^TG2n}<;WZ%dJU;M`Ji+i?xHl%uaeLvW%Ukd)73be;G((B(d z;mKc_Fleu^Rhc%XBrLEDgGiY=zP;n#k+INFL-c%eM>;zH)0E=lVA`= zT=A}5N=AEaGw}6U;=j*U?>SC4S|O}PPe?_WMhd`+umWrN#0(|#yk~T$`ATaasTquX z{sr@eRG<6C6?>N@@?%^5byS&Q$Zjd}i!ACWZpxuo`xW^Kk7`07_l(5677lQm1ntvt#bG1G-8O=n&G;4oB4IL=C2trw~9vcqRbxH-T2*jsQT6D z_)jypLtrlDUePah%@8Mv?DUTBCc90vrn3al4|gG=!mOw^?O-7k`^o}q5x=r?>kikv zbe|4;y}A1_k>lV+thIzXnr$Uy2*x$y2IYg7E^Htw_nJZODx$tmL1ck5y4GP5#tCj5 z>XCb%`FM7qVUWFSp$Gcc#`u<~F@d10W6W2{b1z-IEJE_b@WnPyE4Z0>aa*dh_|g)^ zD;@UboO9h}D2Rz4&ejjgLN1@G7d5d>ldMo5ZS~gWF}C<@lZ3^q^3mmgsyuK^o*~B4V znd!z!PDgK;oaj9sbuX9q8cleC^y(d2kl|F8YQH3G!dx?Z!Cq~%wJ%6tP#$gjZJ-cM z*D;*Y99W!LKIgVH7Kw zN;r#F(tWngb)@)GHaDL-MI#@IUh9^lnq;pv@Z$y)!kgFv9X;_{JedvXtPC%EW|F!* zXLJ1sORB;yj}D6#Nd(7f(Hn_8;l#pqEna5V7@;H6#cpTiz6)e2C-arw)KHZgXrI5BePGUAwWuQU1@irH%++4;L4W;U4muz{XcV3Js#+U>NuVN=qcx6g%fRBL ze3G}LPuc0hTFP9jmTyo6m5~`ZRwZGgO38vNw3KH>iJ)^09gAm%y_8ko+GTlUd1L;V z=anmD34czI$_+{9x9(F+3kBDABYv6kVr#y0_$=xLpZY*|>^PKlm%hWveJ0HPqwGks zUgQjYY>clE(jmE$(zr?GZPTUB&Tn#bO_!+9?<3i6^M6M17d-JT2Jf8aAms|zDBXI$ z_R50a8(RPkltdd5kBWCs+IoK<1UB}q``&B&VJ;sRcdK}ZGM>PegmeyM2`z<2!BY(+|Hxb316D)pJ=0cnDZpX_V7ZuCc%DNskCtFUiVI3y3{I%`pMFk5pgv2wApSpHHIj$@5sf(+7czzb~`tI z!JAr5u~7`iG)`^0O0~XR&J6yoHktYo@>F1wE{+) z2s`@;yR~9EF=FQQ+|e%djB#`;kDAVnuFV$jCfI7zF|f zP|(MD>A5a=OBah19)4glN3Qa|_=jz{)bRCVhWwR;p2ZR7eeofKz&c*{JeXu0~F6)+0RnuiBH5Cu-Y)KFN!`{a<@=(1sJ6?I> z;Z;l1u!^O9tK&XP4+maGz%MSC@#Y+Kn_x-fXM4qFbkCdWy4aT7@ds|efhbplEuj14 z#ACuv>EFX0xxOkPU9DHAj24!j9-Dw&d(8i9Q=e>n&IIBnCF*8VVWmiD8`1rM-Q!bb}y8giWG*g8$D-C|L1;#eV~qJ2(}U>}B#+v@Tj z?Vf7f`!a0zgWWR^TdhdtBfZ7hGee%>LWXuIUQ=`pi={9nL>9dO?QPie_W!;WRUMRQ zrDnL7;Kf;4$PnSC`)i0J3}pH%DENR42`#vaK@DTDPvK`ypZSU}T7ng}cxcWp-D9tA z@2-*e#o-M(>8zP;Yh##BR7+R`^4oT!;77xUT^(HRd4R5LcMYHE8x>Ohyc#yEU%j`s z2gf#E*=4*Lpr*WFu?px^8ia`$CJih#!_#7ehUy{))1~>1-}#CHN7zoyAmvSPg@;He zlnYBEDQ9-gJUj%V-|)_d1DM?UcV$efUvu*vLNHjdlzV;3GQFn{zMK@KJBRvhVRxic zgH9Ngt39|Ywf7(+9$hK`nO$k2WkzDd&7Ah9f7F-JPq@3p-1>&6kad-5T?JP z8l2LkkdozhCn)^nywas^BhkNfdG9>CBx_4M;}^}}wU2fcxm~;mQC%en-`C^ok3_u! zeKz}eLupct$!Z2&EIb9B9{SA&ncVXu*E(qz&5w&8oH7L2G`of!Q&XCq)@fO>#0MX) z>5=LSX;NI-2OgZm${!=d<=zXtbxheb(l;X}(jX@~mhxADXG3Co#Wg-_Q9q(`!)OnR zU9(@!s_yyq&7iETjM^YU-oDGDtt}1d_{3N4yRXik@;{%Q!jhB2!_H}Y=LZCI(N>bz09lrb!cvxxpSw)$Hm{-PoO=LB9=<7?z9(=(MUm6tQD zK|Y#NGn3j=*Hf$4XRK~7cl=h1MfaN_M2A8gk%C+$q)azHdR1hC9!3TaJ_>G8uTtXw z10$nmrSXohQZ5$MuRIKIl<19USredtG{}u^jG8#_)CiFh6%d+>Mrw~9F)wBE_C-0y z+OXbuFqXU?E@Eyn>U!84C$X>h6F-JW7}N*oZheP8Ndtu*~zy*4#MV&Cty7u-_@PL8B1F=Lz^}7V~4veewwL_Z?`g)KbBJdG9XF4}h zymm8Oe$SPr6PExwIb&uO!k=3ZrH~DSf~rhq5sEBrJ2LaKTWLv{jG_fxA2L*1Z$3cQ zn;~hXt|;si%SRR&vT1G^jUO><`G6zL>;m$!P-@l}+5{oqo+U>I4x)m`Z(21T^d{!L zsM@Ea&or)|EK_U*NpH{MzR=gL2=WF8Ku+(hA89pZx^*3v+wp`ON^ zZ%$XyaVk679v4Y04?|enuws8l{2EH?bCxiXaIN|r107om&H4n-FZ>6dk4k?Ey#5?~ zP`9TY1m+o+g%6b{W)%;3>MtA!&brg0DmD_c+)ZP2OT#y;7AB?(B{jjCXu{WzP0^eH z2q0NKmvMYFj@aGOBVdI{HA-4F4#7lqkZFWYOi$h6$2#v;%oluZb-EFDDOr|rE3-!j zrV!o?dh-ui^Jo7Af~2vHnFQSKGbAmlODssh9?yii3B>V8pad-yPj;ixjJO8);8!ZH zr-KZqcld!FB*GzMW_EtkDI(DlGa07$@c?ttD4=c4rCr~9P1@%^nr;1#_s4v3u1lFa z#NNKi>k_12eo1SnwXgL7^Yfs^TrgH79;k7y`0a!5tlBB5zYYX9c-MUi7CO3x zO+thEf%BX2Jt$$`n4#;7Eo&8Ttd;_k^d^7M<)vEzycA&z1}vm#;7q2khR?^7yLxII z+{5Q7V-(A5{f-c&20>VKB>{J+8o$eQK__;l1bXNjIa*`sYm4HxXzl~AP*|<~aDvGx zv0bfp36G{pQDW1Ft3LqNjV8pDO`3)9%$3eqVrlu7Q3~og@Qi)NMZl(cMf6%$jIx$24O9zMbt# z(MI+wD}}Vt$D=BUgm0w3QZC{8^qT)gxfo_=qu;>zZMDmCj7Cm!uv##2I{Zx?V#^P6R8jfzBYssdGNQXJf9Qvb|tAVRhe z&QtH|eu&k3n$n1Ei%gW_jJOuPX3G1$GaTtVK3J8o>LOz>fk^yDR(x$CuHmzFqO374 zU7oHJ`GI$pb=Pl_@A;cSwkDZH1!BGM@z3p&O+6=#*nn@nlQNp+tVm}WP|r{X|+RwLT$&=P7C5eGKZM6H#Q z2?0)jxZ}=wG8l0@xkrxw5_FQ$WA9?(8?k_9RbA4p8ZNp55Etv+X zvq_ZvbMiFonM6v>`HH}eBN}mG0apiRG%!S;VK=$6)C1tId%~AF8eBgwcjzx0#Q~}t zU=lo^+~mI)bm~H!zGvlzD5M~s`(4J+m>oe38kLjhDqpb=hrdMI<6W!8zUt{#W6t$z zek$UR9V&L49j{;gcPOqmZ^N5QOMaz&DtDSc(@eRXVbbM%HlbdFfm&$V)c7m0-Jwap zq$I5Enf`vu44ZBnnK`@D`naKwfRS5Wx`0C?Z7?jkdt(mM*XjH_Pu#}NH=F25a-yQx)73)Ei1NE68b@2%+Qx8eJ_@BGG11TzuMzp&j^6+_t0^M5wIyDvF)ao=UpRfpdWu#_DB=bQC@c^ylg>q4RzW{8)Y-iVfFjC|#Jea8HV5ivLPi|$vkmcjcQ zIU>b+&H|B2s~^h;h`HtrS5x-J35-tlVoxM7oj z&Z-_`&rm20Ya_*KUfTu)^^pW%Qo1SaFy!>~-|^>e_WNz)LMfk?xY^~7;5k;fIog)z zNEg0^T^*tZi@q~CLdhKQ9O;Bz8yyU8bI>v|V&|wTgup$8s=RQewN7_*2pD~g^karT z?fGP3J>L;)ML29ZS%SCI{n2OcXS-7OVcOxXywZJNf;mjfr?}4kKa8KfSm!g2e}!U` zMkZC|8d<$27GfkfkF;unSebOPYRRPN+o+x~RrPBe#VEb{bMSn)s1+79x)j{fR-f9D z#iA=RjH78eS|E7Up^VF{D9iY+pc>gY>#T#!Q9>JSbiPn$VkK&KJDl)+yZgUAH2Cw0 zFK9YbN>ZAlSi<|k45S%|kPfV~4@KfHcvvnrL} z7k#+?1pMZtVi??=d$$RIYGR}1iSW*8qR3Mw?iA5WcmxX3Y;Ij9|EmR^*Y*FO{E2T# z51;k(7=MDlaOXvY4va$g9kC$>vo_CpUy25v4Wlt2xKMAJZt(sZP}QV*LBY zJa!8wGRJQGaQrjJo&A`8$!=65w)Y)iJFdZ~^Dp7BY2%e-ll#or{jOuzvOo*ooE`JN z$vpvWWTvFADLGeIiy%u7za>gqE~iCN=B1kc6LW;Shtm&u>0h0 znmFm)%r)qKf<3)iD_+yQ;B#8(HK`9Q9t26OYbyUC(p1E+VV?O_pc%&s8E&E$_ZhgX z%d9}X0n(d2$sOUNUxU}MG-*A0eU=wo9*Uj;aOqh18!q-9s~dNxWybh78d6J_V);54 zM-+M+pI1NA(Es|U;Dz{iy6_m;Ym@dvCAxpckacG{X`53ZW+xoYa-en1B;*sUQ-t8M zWG%bwjL{(kiVwHrtqAa>rBO1Y*_Ww0y*k^{HatI~gQ!Ne*&C5dumcza!iP-q1GTDY+|vwBT$qh2-l{SaWL8y{7RHx=bglTH0?cH*h5%Rxlh93@MN)w!%NR@Z%reGz`e) zRMjtunP_*9@YzrNNRB#L(p0Bq+5`A3Bvn`|s+$ex<2N-*<;-*5F8v`*ubfcrqQqWujMuc<Sjmo$qL0+q#XvM@O6YjzFVg zg0gnD5hfhi)eKcLYSS?J*Fk2F*|^~?Xt!|NJ$>~NsNL49l@_@J7_%O;VYEAlVfL5u z`n@^%w8J$1>%fKP*y>OUxjf;yVtEOBhE#&G$ROyIL~T_u4Ye+K%}#XuA~$0$=w45| zeFi%LWwqg1!Idv*F{?j~q$1+@i+CkqJY!HR_bKlv64Z1aGJjSVbce~)$I9_viL2%fd+X#aI7t+te~N-#VBl+RBRy`FWe@Zg z&RGnS5f48Koo)NHJDEX@W}Ov%h2s&SC3A>z`K_6=GWNDKW3!&R0Nq{d&*%AxUC*)9 z8$+(8t266P|8C_L#Y)hA>Cq&NpEyZsIpG`s}bUOigoF)xl}r9~F=J9y&#YDP&75^eKwP zEEVvVC!uM?c0t{)#NxToT?gSu7>45bePL&hs|$&#%SP-BSOw3_k-CauIU^Lbl}c>{ znHmvfPoB(1eBUDtW!`T`5T8@wMX0$?39ElN5HW{%>FrlgOzG80jNm|h*M0Sdqj18y zbsp3vH@NRcIdZeK`G3O3&J?>F+j$c|%hqtI`bz2O1GFg>DDXxmzZq{>W&&|fO)tv2}cb4d-FS6Cu z+ddf3G9HuNM=}=f^Y+M2A&TE8TiIMw;>nNx*+bGak0H%qtum{&ZldR=`}YUZAJH&s@(h~s7F zkO{!czc=Rh7=i31`f?=k(zte=@Ab26^qV&9Za}|}L_Rk*Vq?L(3kE}^8hDk2ZWbSp z)-p;;-EU-k9!{l4kMwOrn2&QhS38qTxcCR*NdzX2Qvy9whCH0Gw7Y$^9_xoy=0-Ah z9)ereaOP6TxO3w~Yna_!eJ1n{(!H)8RSSI`NH1lo8?+aAz&-dCj{`f~>!o%mDE6A_ zXp;Raa^jaD1|Ew1#sKWp!P&O?pA51b!N9$LSuQ{-v-}pXU!QGw2GRsPwe|Q0dZQ zn|{c`btpha5!8g;U-2u7u0?;#ig>eePHVzdO}F>KN_Y5!26e?JLVe_%2v5da9r2tN z_RUBwNi5c6!B_(t3py0AS_c)*c`4!yT0Dg|KM%{u@(rrB${Yl(^ z{F#WHGIjpN@%fwM4lO6IR#YdgKsg}T-oTFBSFEAQ+fPH42i|_@ zhjw$Js0hlT-c`EM#24~V(n^goQ^^nuHcqH$a^Ns~d&=EC*uoEl2zu8Uh?rMUOVy>R z@U-Njh$WLX_OVFMF578gd&0=A+Yb*MG@s?&+nl1mF>RR1BL$wVW2SzE)z(+U{$E&4 z^e+lxJS;dRiu?+IW*s7beg=v(sg)WfAwSF-_(ey8=cFt}Wx+(WVPGeBoBTB%(FqIg z5a?KrUdkfI>e$MCEDPSdrBqVSCo7V4-CO6|l}Yc>vKR7x;saHD%xsM~KjN<)c-PsCt&EUnk;pu4Ic~Dq*R0BXOzKb;w2C@Pm-MylHGxI(7f2d%n?d-9;x9b!E+^s;3~F~upyLkMcfvtCg$W#ty23Unuq zk|iI7W0qcwE}a5)`+%GJvQ=Lcy%W<2YpyOfEPj`=_`Ve5V%lf9L`X&swOf-jN6~4< z3vY3LAsIE~=Xuy1uW|OrIwzi+Qv_t|u_6skPDQ)_dHmUT8AnU8Je5$zq6#TQr83tkty@FC^G?ukSe3FHipzvzQY}K-irPy@eLzAY zess~19KE#`u5LcUymXfwmXX2kCy?DcdEqB~%|;B%I7Pk4G-G?)-twx!`^P+$N!sn% zW`(zz6<4iJIjmZDID6AN%NO)@#BcTRB&{k*6R>ab`smKF`k%zi-&-;yN}lUcPmL&_ zZrsOo_cOF<@VGv(z^9&lP!Se|6q>x|Tz6kpT-9icK2u=a#F}V*VH^m(HCZikWW|?g zKIn1su&3DAV80tw6Z<*0)jk;{1=EUMNJx+4$a+~u;_3;6_x1=l>Wl0~J&W$NOws8q5ZxH7&=c4o# zmCgO1Fj7BnS|A#Np?gDu^W#wJx&tb5DE?+%uLlV5_M8FMP*)B0j2Q8f7(8ieM*W>4 z>O%D9fNqLb$4;Sc6r@}sLPCh?!%ih;s?gdHSm61trymec4VlNicI2U)U zyP2rrgB=7wuBDR~)2LfIVdXFfPpgh42~_p50iM%@cf&`OdCI0~c#uws1yx*d22wj9 z*S62yrz%n3vQ9OB^)^yrP&4*qFV;h~jOr}r9-ho)Om7>Tw;G!*^t>u29^6ba!n1|L z!01gZ4oRT#AHD%ZE4h)CJZ!^-TyQyEGD$gO zYUN2$0qxVxnHmmrutENwBLAfLTj%ky8V>DuBU@@)?FHj|48Eem(Eke+hN_t4NQC|7 z>K>XnW-S-VduV08n+|)U+8fpNHg0ScRm)1%z`*b@Pu8e6Kb?v>dk~9EpayTxp0$_ar5%^@MnIaU%ABv z75geG8Tl3@EfwyH?V&Ylhb?#3zH}0={VMeM&Z3}buhC`sZ95EfZ}yg7fN=6X$Pb1w zR3Cd$DVCKQv85&5n^w{(bGnqLD_?9{r3&9@w8ZDCe4V&zVdVBzSF zf7zCj{KuaBfa0ykuqw)Oeeorv!TQa1L*Vw=wZA1ntMZe`IdxKY$LB~~)7G8tp(jip zs7yc+Ni7MDCg4E^5lM{ZJ=#{KWH-dxCj(aXs7h?WTo_}ToS{fS?`#}`Xo2dZ7Ep#H zAW#-!vwSP|jW@2|6GpyMFD8VqOb2(I#@$8kJTFjO^^bZqu7TO%fTESoFBb@BUv)Cc z?HMWwNQ;~S3A>@@D*)&<8LE`!9IN2N7Hm7#%0rwYVC7-^n7Ro|g#ZP}LJu#EsR;Rp zVlIpi4?CSQ0A5 zN6eOnmWTOl#K+ns?IU>l@B)IwuHQM_KMJz!TQ3Mt=aPS9Vix}7cbQ5x)K@fg!Svx( zhKJ68*0X~W2ou<&+@qi@L?>}))yTEY9=VGWQd-tsFQ^Yl zFYfFsuK2j-RW=5PcI$+6uF`YSh-J5o?CplpEp2cPvZQ5f!Ip2l$s;XKBvQnSy zAeIv`krvW`^#E@3p|#k6{qrV>zN2N8yNek34%;uoEWf?~OPHlt$Q|cP0$rhj(zB26 z6TUU7eO{Uj+6e_c)l!YRqe@;!BA*dq(~#gh5ocdFFBAklnckt{TNHF4?KS0JQ&54U z?4*X>HTQrdC~e>6j^VmuTcl6viY3G|BZCt;ggdN*cA#}_tz~QYkSl0#J8HQwS&mD zk8WK!Ql;u+vZ*b#uFvkECVfjvq^Nu?ddncPg-EE5IZyOmij0@dx-gCyA(IF%#_a46 z2`&M`cv?WlYq=;0d8Ty5k;TY+Jn9p38+r6r`@7~yuQIpLFPuupC41XutbJ22^hzGE7Elj@eTAsI(1Fe|o%RvduY5g`-o^ z+~%xU=KkQqIGz<4Fsn(|L=fgGkQtBp7nij6q&#K>=Y zA{{|2O|j>4_L+BbJdOAcvxYu`GG>LU4}F{6k{>z^{6y-3J4mCL3!OwA#5mh}T&C?s zGk1`w>}&2rc0P_VF7qs8$kdXq-=Xr5ghn+cQ zu$tOo?LzBnY6cZMEJTSXpTT7i>f9O73Uk@m7?(q3i8$%D}k3TNHg%m>Lf5T0u_S2I*5 z6kXPwTQ=4fWLt$#A`^g1yXM*UqI)`z(O_vsRUE7mha-*|WgLTTyg>=O=`ja(V8&xT zm7fkx?`gG%+${cSAovX@Dd(%8A578tB0&@2x~wTCe1F+mSM}#K-AyP=MiWvM^630)L;8hHqGK#7~?RkTwkh#yWllDfdgGKJ$(TS3upi}=VRgzrs zw?_H7HV7ki65G7Y{YcbY*}QLlk`|+(2(>M?bhWl6*eBHGEGYsid9|g>xy7_)fYMSv zNl?KSf6REyi7Q>mEIrnKSBx5Ty)v&?mNSp+fo?}w*{_!s{A2G`=v9%Pz=ycUti#og zze?HZAMtskAicK#t59Ow*Htl;*5vJCX3e_!W)y_bWamrCfF?7{P2sqHi%1MMDt8FKBK)=@|CO*LF&{E{4N3^n8w{`D@ z#}*}pIuWt|nDpzK&{BSOWgFnUE>Si0y&jD>NPl=()<>3M@O~atOH0&ahd&`ym(#u7 z$u5V?(k_tU`8F}GCs89T8Kf}8(p!ct*H9+^Sj3PaWJD?UI;H6;Mtv0s z#C#P9kZ>QNhP1^dF`K>D3Br{7=W5r>)y@W{N;49>&QM4ej>=`h{_$SW?mGc+@ZF82 z)kVQqL!nNvtm6qG;5r;i_h+og?-{gMr(`j);*F&ODHS)+s?t zBx8Q~AEe7Z>o>7*Ju8PIC;v8&pZZ+2R?pFfRR*#*1PxO;S}XaNPFiCb@GXVGt@f;=WiYUkj9Tcy-b)fF8<0!Z zEnZ1P&3p+iqHNqr%wBfyC0mnaYU10q5;%3@f|cJ9n>H5a%H0&^0s^_|m~x+C8VPgO z;ntP+;jfM{ge766_KfUFFJXd&-D0G}+<(!*|6jLMXtt|&59M1U zt%O9ocN-yQ{j|!Hw_Y3cmFWCg$|(w7D|clSy>@QPg$}j^IGF}ac*R$RW=!_x_que% z+A*E#mh58%kxt}`(+KiZs97R@)-9i4nRX}_%E-*R4$0E*g>9TEuF||L-E($%v}9p5 zz-V3Vwr#y&WOu{?|3cNNXb7iqw*tHqTDnz!Xnrt))C%=;S#PsBHS z+DBz^PNEeL-`785Y9tp=dkKUHVN1TK(|ZJwV_b$5SBu^Ss@LHWt^y-qferLa19?4h zOcr{~{DKL9SL3eq_~M4IYFrOu2JQ zc@K04sjskc&T3q~90$_u?41E)rr0|q3@d{3*qfZ{LqBFJP%VYplN!1_RCQQ2~!u3=sV5@ZVV~MVb2qQnhTKEiR;71=0Liv%KfDvRqDC2{Z*t z1z(~$ZN0Ttu%?!7#e1T zZw`FBsH4H$QE%X+mel;Dv5F!3$~aM`Hr&*w`;dw!PUp6qp1YZsvJxnk+oB^rlIMsf zrJS0NPb>)Gu!_zT-4_?T0|d~S#Z@JOJEhNd2nEJo8xR^c&yEdCvPTnLyATEo2B&k znr`%K+mqAq%U!L>5=WVB&ahYS5)~JQ!jDLZ@8)wfyl8-p<)Ov;B`}Ip%a%AdVr294 zlZcb^hV4@&u$iA7*UGH29o9N&8977^C+jW`zl2Fm%Q8`VgsgD5Tw5A=sN-6-dYV04 z4{u&DfU7n9EOrV0(SqKA@LQ6GQ;J8ryrMW*X=P1m6!Sn=A$`rB)8k^oYuyBU@cPwt zjE#15G~9l49K?5TO|uKp$yge9fHi<~sO zcDyc%Tn4RmK@JsQqY~PT93@PNtc69d@%18SMUO^&IMUVG%Nle(s4J;E)|yQ1VT6-F zmSx(|O-sY6h5WLmLm?1&o@VGFcgkXteP@MU9OY(pQ+9pazy}wveG$(?qQ|q95)|ie zCYRvVdv(Oul4f`n%2EtqKo4V?8K+Ur%z;dL4yR?5z0ItLo!cQ=8s^+9x4cU zSl=`>qzq_Q9A}5=<&oHDj*zSvw0g$-u*u_1OHH(=pea@#5gbh{ ziH++NMKdMjZ@80iBVkiw7>=4(m^&mip3du(RbmJL0f3nQVe_k$#b0x}f$gn}0)a5` zeeEh&{ABtcUBUT;XAI3S;es(uj_F@n;6mT9z}0Ok>4`B2Hh3;*@*G(RsM%wDTCVChTItF$(v4|yt zarnU(amCsk?A&e>P@>&>8vcO4w7EL?#gDMc|Cr z>9u^*8M%?CXdVagFnwTYQ^a3u3~5b-hg)gY#)+VbYPD?B?@tZSwO;v3bAiRzV5Z90 zUp1(7d;4#6{ka{4v@$}9?a}F7SI9NLhwr@xRrfG&&#liDvb@kQBB4Hv^D*guokE!t z-$-%PqKiY29J+C3<9sE~yTWqBu`TDZ&w@qY*I%<#{L^Txy44~$M-d2EwLbiQDa;k0)xz}S}Q9A znWflPf-a#&9)a>fR7|W47DQGr2jSAmpnPNRK{p}Qgf<{N{BX@mWY!rf!ENxMBQ~Ow z7!f4kCO;V8RbfuchDq^g<%pZwR;^VH(YevF(<{>nx(kQRC8C4H5jU%z!t25oa7o=N zU#Hrk{|*VB9xpt^=k)M~MB%oK25wPogO8xB5xGVL>$lfrum;T8pW271&&@#Y?MW%G z(J(ZsqVzl>ny+VfBaJmk27R&tmR95v;vhnM?8KbmVrOw?d`s|;wFyxl5{dRW(X0V$ z%+2u(Y`!1uFg`4`L3ektq4=(dGSVduqR1#Bq0ukkT%9;5rbww=2ihHh0 zD{aiNCJ{>=e|-Za<;n-j$|nXUR>++tk4SV1H+1S&?Qx?Z{EJx;eIaGUaK&}0X8o8l zWWjnN*j_>4n_OX0+$Dy0w1w*sN3mExEA|eyN?k8Z-!EVFLRfztoh$U_OzwU}AB6)k z-0bGUb>G(InfryyoKEax+7V4IolPuD#Ep zbRwLB%NX}ubo3_8kko4flhuuYh$1uz;$~P?4p@*<=wTmm+-F&#uWY~ zenM&(=;zRi_UrurZ_pyGpS#4yUFzgsa-L+rlz*zDX4Po`{k=hulv8`rB9?f0g8_tKS}}QY2UQT_`STjN6Vay zxgQi~w>{;byEz#1BNWRfb{1sCcp9M|zJmq)etmvV4Dm=XR#QhFk21u*o9n zr%{g{{r{)2MXo>O?CNa!kd>FR7UB)Lm!2qESg2;h^L0w$KrwcJ7<>mwh1X z4lF7VmVZ?HtGibba*Oi3nB`nthI!L#>;KPdjWo`Q%R#Fv=6ko@`Uz0)^;gsj{MXbw zMQ1cdK)tX})Vn3uIk_&ne&Hy;(-G9&owwA;c8BNP)opmRzf6*l{`HBhLA%_mjO!~S zH$J+25H{h$#OJ7CZaV{GqXnzoYqjzaLUkffSEr+ox$@I5y^DOX9_x#8=6~=mAR_af zHyLkfa(g-oegXwBH6`qGe)juVOI!O_%iur27Pu<4z2AG*H-|L*y}eG4RA_#T;n9C3 z{PUy>di?iq;y~abkoJ$(9{pGGFHq>4b6x(4h#=G~{z-7buh+kTPwl^^0A1$!iiEOz zF2y@U#o;KyD$5#&1b1aLOz{$q@K8_6rO5o6{{m z;!TlCepKI}*#rWf?qOEnGF$*U@*xIl|4UkdA#>oFY z5$jMJf43mcvEe#3x{dl|20RfGnE5UI1cW=hRy#Et1c9v2Uisy`CcT`3-emTa`*TC~ z+ar5GNFF!@*=-b4Md$qavS)2hQVtPul6Hmzz20DW&Z+cb=|*9P+N^RYo^E%D2>nm5 zC`>m+vT9|gEj2g&r8G}GX(X$})ly|KMV@}xsP7*3|8l@?F5I|m_Vdfaw@`VK3yYJ* z(wSb|B>I^w;{KN}ItX=@W)ZS?3|TJ+mlw1#JLz6w##e#U4Pa7T-KXG3=!HNoPCgYb zZ{#RjD75nP0#;VkGS2PD(%;Wo`t#!MhV~yNB5tH|$=kj8(Uv+r(B}AXT@%{>}yb89h*18~P7JMmF?ouS~Y!+tNb7 zEYl|q?nT|AzB||#Kg?PTSHWrFPtae@|JkeQhXp^;C)w>5OA!9kOacV3V-=$2yXIB} zCYZd1e=&WiYq4u%63<7F@9^WooplV6ZElucx*;6}9`lO#GaRCiMgc#!p3sL)Sv=gz z1Mkeg?di+v!;Za>37Bn~O(!R~6vwNAhdjOWt#6jKTRE&3&bs{0Y+dQ-e#kkF<;lGV z!T6w%FE!JU`9^pAM^&fK<3G!PEO}l!afrlIuL;$kt^lTyDENzB9DU! zdXOrDmt8byw0Hdy(%2Pg8>3x5qk2i3k46en46tx1Tzi0v1lG4eER;TT2R5D#%Gdq) z>tK|F3xp}41D~z;i-6kL*)O$~gy%ec(9aL z;0f;Up7H0Q|Br8YmEuB+GZbc=2FBdKIRK{3X|dZc?`4jPV^resNW5!YJRCgnR`fS$ z#3#}J0g-o%KDrblqP)7`Lm&84`{lr&sr-%MW1j>9$lqLy+seZ55jvf#G(Q9UkDq<- z$^rcp=hlDu(RppL{BHp+mdBN@a7nj-N4{Bh(c}#|~{nZqi}!3O1k5km=*sLzE8-5M~tSepgJ01na<6d-H)$xqXYWL}&fm z#`;lbK_w;&_E$%3gHt4SHl0b&g;JVCzr*gXUFY&CZlROcAZeckhd#a7g}E9DxyL;_ zz|IW@z#aW^09;MW#$c&GRj*aRY|iM58X#Xu!yD?$abIBEgtM&4MKEi-$kI2)Ix!Eb z*|pR&Uf0%f$IfV`(#tA9J=i{+WSjZHEv$GO_vdNMPagopD1R%$mp;ZtueZK)g70nI z8S^Em1OC2!nBexLpEBZ`Ri(_YI)ReVE`^?3;qW8AONVxj?EE6=``61_v$k|~Hlh8B zPTl*A^!WlteQc=jBB;?Y+b1)IQ6FI86j-UI>{HD1A9&}*(-h91Z$Z$QDk= zSp7ol82uL(p{LW>)1+(H&*9w+D&Ot#pH(aRAkG4VgllAI-#)@PuW4^m2zj%Jg{83C zfjcPuKnp_ccy$}|nb!J?E*9GBIAh>4jD0zT%6f&eF_H0OY;2sM3Fxx=mf3a)Mztr& zFHgS+$u4nQ2353u^SXC~W#eH&#y>y^Us;Pz7+wov|IpnU@~{0%-(c-NlSuz3)a*)(ZptgO;&qO`f(C4U8?<=Z3ip8OX_lVIjTzOfX}Q<+T;H~ zE$Y0#CkEU=hXf6MN6*GH$QM3BvlnEji#>q1Z&)~TZ|d`e&Bw32C)B_)s-6Mtfw(P6 zFaTd-qvg#0+5u{C@fx4rW7ewR0tL+ht%=+Z)_T#bGE48n`X20LydB#2Dp>xGW%=X{ z^R?Z~ET(Eqk4heSJIxP(!d5WkINiTgxj{Gfm(awEp@w3WL?itQRmh%8-?(Y+6((FO zNnuSmz2`QLt}xEeF+$E6Yzf>-{Abt5qj9|otm_R1B_j)Yi$dOW#vXbo@=Y|@y57_f zA>cdj=L)c0aPs$Kc{>mJBpoB1a<@rk>H>?^%%>UVF!PeiOk;PL#>6jp44|DLCFspy zZ=j+5l*DneJkoSEV{IEGWRZ$)&*;Uqx(4Ec?pDSeB$@
Nv}0`JqyXl#XRvm;1eq!&@XzomS%(o6WD^D_+!9;GP~lbKrZ(%WuFyie6skS3Q!Jy(EQzinLeS(2fbDEW+k*G^*o#EOe1jj2na z`RVF5HqOn)>8O8#q-W6SmyNL5JFZ)&!wq2mTzTSEFv(1{(osClNcI^MfRR?J`n*op zYNlKb?Z;?k_9j6C?&cbRr7FNLGvb-Npk;jChZzYfN{sC<7d}2w_>y~4&UgT+0YX9Q z*8-!L)J#avl$#J~iJ4^a)aQZycY7)U9%t+F>GNzS|V)#%O9PL}P|h`W1`WvMVL&z57w_P!>XaJ$|a#D$#+ne7uM%PBY@$IDDe?+Tsu* zw+}LMy5e%bFD9>=t%l*OW@%O#*Av3_NJ??m)01MYuzvU|W&Mvu^c*wjCs4 zmArnP9Q($j&Tacj#ttoH$Du-j9_yr~x4hhMupUSsbUabuYjmPk^LB{$KqS;fbfg6( z958{2EuB4$lXG`wSn&mx=F>H{M#3@* zlmXak7qM!nO5)jbkFElY*UWT|sDNo;*SHo5zvjk^t$4nl-sQ=Yh>FWw%JtG&^{uq; z_9D$0IQ4frnxT>Y#YK>sZ`c4OC)3oh6xi$@FtO~OF}Xrg8O%YF|1!Q71U7MiLChZm zcD4y=0kdXC@wahRMuKm}W;oruI9&P;7tP{XZ$C3&zSKAMpl2~woLaR|-YeW12j;L+ z4ojzTxt-21Oewlu|FfR8PFHT`dyfqM(>0Iz$ZF6ddL6#}WVPy(NF&CO7J=~G+}k~T zgVGHQD%1GXZl6l{^hjj}GO+mQL0LOJWNk*_L$w(l3DPrTW(IGM$hpj%YNo#QGPp?% zXMb`yEqO$_ppl`+m#{P;L$IoUWOG5Q#)1%!ae`vvWhV{0=Sa9+{v+w5h#|DS55bCW z@?|t2kCirQN2~ld(BrVcbC|dDh$H8E)b#kjb3$qAPv-VjxB_e5`F%o#lkd%nnuMjs zfriRbw#=g|*|hfsR=qSk+FanfG9BOwM8f&cTn_E`XBMDi3S-nuxJoIkKY+i|BrrPZ zX%g3yyrCI?ow6j8pS0wD7Blq1hbtXKpn2r>?qW`3Y`U zC#mqoUp>H58g2kD%=Y8ZaY;-nR`b4qCZb#o(3NYTk?ywcf^_P@c@nw-cldogxf{Ur zbKI2}(udBxDeXha>W<*7SLB^tgd4gAEPH_|W4~`%=5ld5w_N|S<^<&KwQnewCjW`7 zQM}ig#8o2!f`C9r@|GhHZ6L%L@FdH*Q!9^GHO6uM8ujUTqC?}Pr)R>uvSOG~&unaZ z@9s(D6_)pWSov9gfE*7&@10?pE=!M=IO)CBFfN0O4jjACH!Ozs-dOqhsBG5uCm&=Q zN4R@BG9<1_aYBh=qrGu5%e+4usy12ir_@B>ut-UnWiJ#c5k_jz*ltrXy~O7pWbw4% zq6W5RUjNz3LM_AEiwQ-e1ve4Fa^gDZ0olPREiA_?7xe$#xjRb%{Qg`Yusdl#|=X*p|0lj?@D!RIh^B97iL=GujtgfsY4@70?!9r)?jo~ z>VgLaT`FMLbV)EBYAO`R1pSP^^kyz4_zF7t5A@|u=Lk^@QP=WW=##g0)}>s03muOp zU?VC9aiG7VP?+6_cP<`)AK9fjEnYuC7-1`i=uaYQv;$}jQRSc=!(M<4Wo|ZWhhBMY zPh195w`;C%Nqrim6wP>FS$oG$Y)~p=qZ!Vm&|XETHf%3r!*(q=e7ZorykEctBATw~ zG=dWr{&b>{4c-cbnekj+Z7il*Oq+K|kMkM9-mE7!Nhv<-gPfCXJN|ahAz6td&VkQS zow+qX&{S|0X1ege37vu&$0)8j@$hbiMtqcf2i@Ru8^2J{2r4=F;b)yfFh`;y9BkVQ z$>XX)IE%zgyt=lF`2Dv(^DHPF#PhljRA(`7@zTg7K1xM9s>^?v}ys>(ncpt8%DT}7&Y{wMKi$} zX`RHZsEs3HTCW4ta-d>{G=ZJ-MZV!v;I!4OYrFi$ao%j@ID1PkPsS~jkM1j*=X0JG z_M8Cz{q>o3r&fasdrq8L5Uo|dB4Q`90SF6=u;FQ7^vIaC7fc&!h6=uZELg+#;_>a) z?Y~iSxeFnuR#`JDj z`hySHXJOMaA23r1+~_Ej{CK}tUt1op@ND-5PcC53wQ}L00W9?5)vp{-25M?I#K2>xaS-fu02zybZLgvxN&;d%x|9l$CfC0yuX)8z$gYE zq}Z|6+_{dDSzRrlvZeVMd+jk=+eYuIfM+DCa|E(AX*Ph6$obT|pjN;kA zp>DMl$-343H2zo;#xb1vsL@@8Q*z}E-=hE$#LYSXy!A_4z4e)}Eay7ryjJR{o7d33 z66%$rW#C{}BNJshWEcBc`z{i5>^aL9QscRB0`H^!DWdDjdiPl2OVh)7tvPu+Ye9Bq z!|K8?!S~$w<;>;dPwJ0aX)qd^w6Gr4cZ%mfdG!G7KW9gK#g89JRtF;pXc88o@rQN# ztuFO7o^Ju?re%IKKuOJ)5($oU1-o#8E~|7nTGfFIe{uMrmP3%aZ&+Dz8A@emn8@3< z2*LE2LfJ{o<;hp$C=isVQNzgCG!LtR~OB7rrbUvd{MRG3Nb3y#xFMQO2~S$y28`ZkMpCM(1$Zg6BDpW$fNqp~*)~y=&8hJz9sh1*UI~ z*1qubc#_EfS^$&~LvHnMDPRs*Ts9%Y^%c_CsI?zSS$a1>1+KIW@z%{aLzm|73n<(`BLzd7z8jo)Sos+cvNqFJ24>AEY zF~kmkdxCpcMS5U0%meL|tXa#Z#rcq_-Ww|olUPSjjQ!A{y{N3-`q>5FJDF<3TZT`v_pyQD!?@&HL|U{W4#O?)8*q5IV1$!Fhq<(w+Cz7{ zjB!o6;G(gq8~l5xa4|q`eWXgxdB%5pZl?=Ch;0GhKYk6lsyOB z$RmlOXC$pHsT%%|0$qc&Vqondeuq>I;Fm?~7JLq4>DE$6OjOp!aSwG^T`ic=qIAivgjT#z?_~P^X76ZWMmx{#^%H;IEmU?YZ$<^dmg^ zGpK2X23kg?GCWXzBh@ZCUy%18o}aLij(?V$o60l^<{n159z_K4Io`2p$4T-zrUqjj zR3}_bf*EV$&uELAgZ9wQ@?cs@H&Ux+)Vnsdd<52Fvmjt?XEIsgx>_BG{7rhg667c{ zK`Y7br*4~5kgaAIHLT?U5EF;oD;(WQ48e?(mXDrE(C=aoUi9l3e?aRCNpyh%qaX8= zsxM;a@>0rdWx3=*Cf|_((y_!7ZT3zcHD5p-T$Xqe_WO8j_{5 zRNF%mE*0!I#hw`Heb^KiD+b~()|_qzhGgl?z&DH`VuMzd6}$3`W-#&k9B@kcd)(29 z=no367!QjL!pw~Pa_GJI58sTy^3ff|VPZh1XxH`f zqmJvy^9M{TVU7vE=utiDXs7qDv3zfe?9YEqc-V z{06wQ+`k_4vDSv@l@<-WGMJb!dM*4~oJfbd#5nva4|M7w4us&UJ1gkDni_ zVz>?*U*J|%nnZ$tk13!E^dxkr4YYEo0Yp;et}35bH6Xz}_FJ|RAIo_D^pNFn1oN%x z$A_wp4X|sC$XTOgRMDiYGc{QsI_1bT^WPlzyRO5_QMmJ@x<|FO{W^2XR}RVEo6_EF z%NbAabh6=nK=+YbS+-=yfN~Y{3Z2Y#hgMVDdFM&{0k_ejH&9o{ql?)+@8aEhy`&b; zbdipk_|IzR+?0Ny+A0PrX%get&wKAY^o5d(-p(X&)=%(7u2Z?lsTJ2wJMBW7c{X;V zNRMV%txImTsOjo^znG%)dmI$BPKP(N;K&vJ7bIfnAF6}*)1HH3azP$ifNx@^eg^u3Uf@|#p)Z|Pr>e>g#fiRo#!rHPnS~W5~-oanL*dbNF%q919 zMrPl~Bz8mbCD}*S7FvoPm;k8F;GEj*ju!`^1(40kMU7R!76!G0#-4t>bs2)eKzI!` zi3oM;OJuDFiGE@CncGFyU3p`bWx&}PHz_#|3Sf#I`rjO78XL|pt}|3^FWx3{(;d70 z8xg+*O52p?pnhnY|2v9YO$}S#b_G=qkc~{((rp4_T{QNsifY(i@=U+162$T@N>5sZ z$oP-X%S^vOERQOM?m^*q2Ks)W5fZOpIPOuu#YZNJx%hl8>2^>~Y0$nEpB_Y0Wz*X( zYNbdzFM+LnZ-g+fgi`CE%z3Ft2Al?Y?sm;#oFErZxbK?cbNyBs!?`|68(C8{QRzOg z(0|^l6WW7nyORvInV0?=BRle<^nHA?N2St>(=t$eCZ;4wB6JFr{!obn8@ z&D@G*5bHs?ZrfpA-gig#Nck_UguTD_4zt!FYg}?oWCfh~mhZ~bB*plkqFu$$*NqCc zwPt;YN$G|}@7XN;YI)U2Jv7v*oYNn?-9>9l?ulD2^H@Uro$JhF%?9%ZbQRlxq%Q#U zN!=Vv)YDcrN4c=FkdSCp8gLLgw#2#WDLNs=*YM!}a8SG`c(jP}=5H4<1>ZhD=7IqN z@l*O%=tj@!nx%tKmmR1;vc;o|8kXoyem>cozni=8Tno$tR1`^ViR*yEPH&;S;Mm7* z7z#Q*2kdL(r^Td;z{AfS+|1jzC}FdjqK1j~w6pT13hep0>jN+wuoP@-WpKc4I0VWM zjce<-m)<|EOBoTWcm+xj6C4cbC5UO89kN%~R?p(>`5p4#$szVWvlmeK4*kq5q}N5^86lQU4{OS$=RgH9E}hw;9PjYyBWD`&5=N>4BIcsg3*V{@LL3@wNACb03tQ4`|Wk z9J&!t@+O8|qHCAFEom2kbvCLKFRp@sM=-(>%%I&U0=BDqKPi746b}DzX#MPz8&1*p zhR*BhPqRBddFp|R0*N!I*eITYS9nfIYBkc(Nyt9~4k}snKi#1H$EHr*?z}JSN84^Q zjd5f$PKA%XE6WG9)V;i<%pceKLCG(upPf=YSV5$HgSf9;1O)IiSqU-jnYBCb z7F@mdeLkS*BZ1lMa-pti0#Ii(y)NLzpNWm{zU>!~a{`4EaGhchPMHjrUfLxsoV*2! z7IkhvD~5(z%%ksFDkycRaDZayzpnAGmY=Wk>1M(uZ<@Q!5a~8v1Q6AThypJ)A_Ys& zNl5RvdAEgY)cQ5j{Ab1MEje8bOpOW%Fbe^44)!PW{LNc`Z|)umSD`hPSq&EyMN}-% z+2#1{6)-$yZtaMr!GXpEEvEYojIT-)O1L8b?BM=*j9ar=bt)c*6D{M?DQ&Zq zfu$5F2&DVZ;%}*sU52M^zACYwFxYK$I-sZI5A1@pk)dPUlA9`><&5HL{Ya?@Cn0`^qA7DGPkVt2qM}c0ki@1K&g=_(nrB7{zpE6Rh=GAvG$5gMZ zo&}97t)73+G^~9i|IGJ3{B@0C(|5pT>8GoI&+S4b&HoG$@yu_!c=~P12FKr52y_@S zzvpw01!wPj`w94ueC(o(Ec~*H_l?az0^d1t?PyZK42G$#E4K?o_A&dzhhRT!gV_98 z!unr4I`+AUaJU8&%gi0u4j-j_GNo;AzT+K=m0puDXx8}esr=fHFGCCf8CKU`*N{_N zE-muYEe*~(ZYW-ho+DDn}^uX*t3+C%VfvSG>{O`qjE6?Q) zW$s;4t+}@JpMvMlGat(wZH6seR0p1q$X{WTZv*TWWaZwKP{F3gt4jn~gnMH6JH}DROSEkKY2|% zH=O>Ksl~Ye+$+@Bsdx2iXd?4_JvRYLk`#88a`aHCQDR?(b?zy8OeSqfT+{BF5U{Pn z|58uLu++~wdZ0yrqbQ8uE|Jo@Z|?!`J9gA73?gD(-rB&bgO<2j_*3Sdoz4J1`8@Zw ze_Da~m)7XNJ@#3%!r#JN^xd^$w8SAg(m$>pI0@Nvp^#TtUpWfwxbV0gBKYL3FlYrj zwsIKrY2o@`cvn{c_Om{=q@EQV&xR2)gyQzZE_`7ri;S%}!fejaJ8Uc$5 zk=rIU6y^Xt(M<+O>^q_wPOum}LgU?^nrirZn~bClfGen{Z)-g#x5m9qIj8eMNjMiS zNYXula^f}5*t5qt1xmsDRQK2upzLyFE9drTTWZf2F4i+DM~fB8uHNsD026wEi6*qk zV*MH+N~#!QH2h0nwpHeZVO6Ym|{^ap|BB zdvC0`m8*`*28GK0nL?ZxwJTTn4EY5XXLuhg!9ZIJ@T~t0U9guYD?#u;4&+i$LcO$l zRnK?SignM0=|M{GKziGpt(cB!&_)6K`8_ZMU8Vcy!#V3*H zM0PoqdXLZTCQyH=`=1JR5#a|6QILD;Q(NQJ-i7x8wjGH?1f5&Xu?$wrOuatXCr-RM zl<2uU*1uANqVqOmf*kz3&uHDZ9*;t za&}94hL)BON{hPjY%eaimsV>TI$LP}qNL4aj1I-u4W2<6Ww@%`uH3=@LVm!+Q0_3DN=W zx0ipDZGv?lQeAs+rz&rDI2vg_cx)x$QLSmALt_=&oMne1x z22K;}|Mk^UUZ?F(tcEVn+*YP)-NZH^P9yQ}$#Z0#`v3@vGmSml*Y%*RjO#ce{>j1( zEy9$4&~=W5ZowM#;iA(}=B!>Ft66Zwov5m-7 z$#>0-r%hF=3azy3#*6AC>_g^Z?`LWtyP^4}rs7J<2D@BmS(o8+%T8eeD`%-bZ_?Gl zhzQ72gDqLsb}~$3T8_W(WPM3zR>^=3LtAWfu#Y>z%NBFJ=G+0W!;?F?Mr}8a_y%hc#*i0PNYt&)<-opS%dXVGx4aBHk&pj(LnV z;4MIb>P^q3VVvGc`bWjYd{=nm2Bd{j+{u-XiE zo6!p}k2Nd7m4i2#8umaAd=B*+&gTd2fUn&f$pkQ0MsX}qlH!$fI)gn@-?lhaGHioP z*tAW-#7~%epX)ly*xpPFA?#(<0+3GP6(d(%A*jcb)KiGIT_y!dG|)ZQJ_Xo1LB{Y| zCm1<%>&#|%F4;4zdi*PIOOnHXc2~Z`@YRL76Ctx^VRUJ}ZS!5rkrxiN6+P-XeZSw1 z340L~IpGbWD4mpb&iN%^Q#uEc?S9sNoXUU#HjEMZriE|kgQjp=MzGGsJ|4i?C7Z?U zP^TrseYfgvmud4Vc90(Vzb*H6Q{ea$FTp2TB}ZB* z%>%M4x!i7bfvegLa!+Ejyu`YOr7C|F9w{FK27+C0mhjq8D}91NAANYh^ZWaH%g~I3vP}B9CuGg9Qyfq&_ zpUDutHQ{ktPzvUl^?Lr!1YxQ{WLdLzhsFLmk4$NF8g}K7_KZ8Lqe~vplLy^=o znfuCNj>A6(#;b_kxR}?Vko$_W?XZFH%XP%8s%dFSme(khI@_j-GM*#1+tNGWe}Awu z?^Vvejs*kfzVqqHdH6b*ZRLC@XuVba&dT& zhjVmQV#?A-D}~k55|vQur#g{8gE4t+j2c+hp0P6GbUL1*=-fuqAzFitmf&h{^m&Cn zS7OvU((x#K`wNMD=_U;uC2ijHk(1$PF6AzWrm7;!Gzvbn)R<5f!2eWI(O=#ykWX(eBq$qs8A!N2hemm_|(#KFpB%(lRHVuwCN}U6-_= zz-XtpV%yhp@Gws75(RDZAK@|*q-F#__Z+#Dl82CN`dR7ichPCcGVHb0Q38v_Bhm%$zbl{v3-Hz$l9KU8*ZD&nZ*z$Q?j=4J#~MGAeIct@@oa zV&)7#*p_if>t57?o$)k7O{EEzP??uEv)qZy^YE=Tr3F4?7}BhKS3t4XV(78%12EdWKAZFU{`-Z35 z<<(+<;M)MfKX!t~?#)`ir2qCI%}t;|&`ViJcUymLgY1^K9ZqOztdtuT&{3dD=(+?@ zu>N!VacV3h-YT*aFP<@x`&@>Ca4{Sq?rSqB=Z&E^yiLce9^-QIfXR&Oh?P-gJ&2Dx z{%Fs49e|_yMtuYu9OY29FnTcP#xUD7ov}esb?hwb} zJrP#DwUKhvwZ&JcQaxH9b&dLTVy-OkoNl05u984|VIZ@$=@8>ff^_90kC^9*0id0v zK)3lFV}0domgwr6R&0s`gL7&9yTy%i{p5A}*)sbWPM2QVmvjfV}uy0c^u$DYP&n0GGAKh?0Mwo-$=$+!SVgSYEFYwMhrk&AYBw{l$wc z3k8{|-@IK+%NIUevn4rl{<=Q(+{$!-k3M7qot{VrT84|>EVa<<=qA>))|qbV4V9i3 zS0V?yX6xOz=u;;>yNs2y?2ndJ+Vx#eGbFRQA~%ZmewwG3M@J5)V%)=%9?+*`%x=J& z-aWR~)Z~OwJXokNKxvHY!Y@pXMW~i8;MXH)4?XM<`TJh>thTJ~2Q12fdD*~9Ol4qv zNvVJbbZB~E>Qo9~gxp;9YFqM=XZ$-|6(st9ZQ@&aHtOmz0JS+S;o0sSc^KY==&{Er z(rH>ZaMC2;W$Y7;PGCx9&yy}&?R{7w0rxNyq6sM6|q)ldyqH+_mz2F8# zSN2k;gk50Q`{rxBSZtX+YXaCh%@6|f=<=c!&(E;C2?F8QkEpovB3r6u2I^2g+6c3` zZk<~z;y>$O1H7>mbHdbB3Ie!;Wl!FkM_TTb(=DVKI<1n~n8>c1wpPe>hj6G(U(hb96qFeQC z&+?=6Uau9hylqE6owEJZH4Bv2U3QLXXIIa|Y5oNm7ESLT8UN6r?LEj+3Wfl9G~g3- zUYPkw1DHa0yGo=bWx$n?L%}gt5qU$Ek4UPxTHvV)4g=d(Gu5BF`*jVvJm)owU+T>w z8ACt8x7YYqfUGC4+*N@5uy^PX$kd5!Hop1n^7gb|*d?BFmVK{Yn@$WPe#72q3da zS`GB{a#9?V-CEA;lb7y9gWVo!S$~`^aJe?8qkTF`(x=l=N6-aU8^Jt^M8bomJzK}M zb_PLfM-p0i=F7mWNR6g}lUet;3Nx$;)@8ZyAi>k@7q`_*F^wJa3Jj_OEw&>koz@^v zFH>5g!%4?Ml==N8Z}&Aw*gX0s9$tB|e)lzkTE~ux^hO|LXLN*%7ZPdLq=V(gIUUMR zR|+_i(di8jO8J4E+VzQ8cPoi;Xn`t9+)qlFlP;l{njFgCJPCGIHELQcv^a90k@!6=|&$-?QZA^xOIc9hj z)$#c3;Qgsgsjm$sy@ep%O^M0n$E0XC13b)jfJE9RaJ%*AI14dM<*kR6IIZ-gf5fl$inl4}Kb z$KHOD#WXkyFA+$L_b}-fSw8jXxAWh(X)FTlJgaOR-!hIfy+9fd#&s<}LVGdRIfbsq zM6WeH^|%q*uRkJ35NI8j{$N|w?|H44n~rblu&n3&%9iSJQ97@*3KX^OrFA38H^646 z0|*YVJh?`1IJPQ$?paODjaxA?wHVx~IXY)OS&n-x1p*WjYn{0v7eW(($pR&UMeo(Y z2O!;#%vnL*`zB+BWsbX%_v_qQ9G*TO0Bq2RumZ5vRJPm5X}2MmoAK)cV_s*Df1j;; zjmIB8=6x6Um)-&M^JaC!!J4{AVI!dcD#m5do7GjUw_h#gQf#nmv)*9;Q}xvwgJ9ix z;F<3VU~pmdIu4lQ=g;e-m`lBXDSdtppW&3Jc%pl6a?6Ca!o*r5k?NQ~U%u21;!Bfv z*B8nM>^Y?u`y7H}lNfLqPp%ta12voRd0oBZx(39b($AyE7a(?a9`I>N@8nu?PJpP1Lc|pg>}!@#U4ZRL!8#Pzz_|%9XbSvbHb)@y?D7IN zHbE4Kq0`h~%#}MJDlWIkz6)mUeFlbU3?LUz>AA2DWfsYk6kMARFTB}pG{!m#%E8^P z2A^nI-i_h`&2{mvWOUviIsUbrsMzR809|MNA7Q1%EPUSZ0DzDEUIgZ-WZaQur=oUy z$x_vX*_D!=Hio*3W@KBsL|RxuJ@r*q2Z$W5q$WTuTjzE6$;@7#-8TFrG#>`E4s+o! z9x)=OsMG05-4Q>+?iy;{)R9nu&LwvvF&1E)8VY;-p&g=aeV~ zx~0EnhhE~q64X=7ho&_cegwWKWDjbSd-;y_2g|9}luZByf);VB$rj@r<+flN%ZHXk z#K7WMTiy@CE&ep4yQr7*d<*wc>t+}2`@uvl(O|iUOk*)S8CmL*N;MY-yu9pudg6>( z$IxpNur`wxW7H#-dE?qR8O2l0_(588Vk4?$&4Z1^*rK(7bdN1B&wFp+*528bWg2%$`A|Eky2cj+`MdfsyKg%bF4a zmh2bibTs!wb}NLC(=%FsZB@;msq}1Mpc0UcBeiP=93+^NinydHP@=6DyDF|Ms+z#O z;X{=<@NlSWE)>zFS~{aCOXtan&FPfe1Gf*QZX$K@q@Pi#UcMJwb_VD=r_~8iU$J+* zgerQeunZ$vu}ip~r4n)Ks-lMqz>AL}Jhy-C{^{}nQ*U<6s8IVv(LjIP;CxMvxc*BM z69%2E52xG$nage;Tq`xHWnw2;? zhF6uEd{1UHy`a?&=!uz>`GaZX6>q-rbn5HCQmWe?$g7NuiYtMRxrsp)eq8dh5{)f~ zs_9~Xu4uDmLcSiUKDsG-P!cd(mP}m>l3fIIy2v!Ie3kqC+awjo6&xn$Lk$QZ;rOmP z!1Rp)W*l2lE?G4Z#GmHJ2iC|pvUI*?sc|vBAGopAIQU(nCK@V_CTenz9Np^>be%~M zJym@(>S||*-xZi+xO3L>8lyLQ`G$W?&xdHjq#%3GF-ObV4pFSu2=8#&qm=LK?1pFK zrGg`Z&OPxfJqZ7<5^`80%3+yB9w@U7?e|o4#`chui|U`*bc$Hn^&sCr~7EU)k6pX98#^=Tsc(Gde+Qrv~s)5`iT7L4v$N9dOAsDjHt2dr>&=)Fe@|b>2GCi+P?Bp4@K>|B*fYY-m zuAqqbZO?+lf-mjnkb*;<+k5xDar|d8Z|DNRt!7}XY)}}xPT!@LHd+RnDpUJ+w=0PA zfx>UH{5GJUOxFJ&Yi}Oa^tH8*+xA+k)^e)?j!*|c<_aPr1X?X3layJepaLQ?L_mfl z5UN(KARx0$!7|7U8io*(kUBsJNFuWkNQ3~9Ap{aafROM#!QT76zw2wS_qQ(pbh)~8 z@i}Loy`SgVlk!omW(11hY&S3mvy6jR3@TOP>!XJ3ky^cp@?ceTHD2}}~Lwb>)@g&|?TspdEElg3w zB^Y1Y8t|M|LCW`cV(FNYy$f3WJ?)={j@QxD8v6(=eoJ=5Io+9_;B)w@9^))@!Q3uE z7DmFEZt+Zs*32dZmwSs;uVz@+Gv9bcJ@4aEx&j$&75NwYD}eJJ=o%&-0Oq4$fXmNiBUtY=moz-B-z z2x@2OPK6zd_6x2wW#s*PyRm{m&ZR-8N#=HFqI{^w6zZWGYnyDY2^V>C5&HM-*&l@! z>SnW}^~UpXm)FNn*T1u_9lkVRAH*-pjA{4lFCNS+;~b<1ML*vo6mC`Q1m`V9`uJg~ zu+a^?$GY}5DRw&ml9pFj+RkiUO^b+E(?cxu&r(T!LaMsUplTSclKR^ zfZ=5qqUK(Gx#9ky4@BFdscem~53(`2pjIMR`?q?=?z9t~4eRh>IrQ(|5f*)vzegbjkvlSf`JruSmd=D4 ze`h`HQbNyB=&R_u694#RsI~czpa|u3=?isDuDHVeuj$2wCVyWf%V2imI z%qt1Krx+{{`s2i|1=#jLFfi=x7aLkmFS5zhy`;-4q;0g|4L{I7jDP>P{8kEv6*ZWl zxZe;Ysu43B8d!S|9{^)h$2PO;n=r{GvAcV%lSBj?yMKu zL1m@{&O+j5QR42rEE$pO)gED}i^)Z#_VKId>}NM0IQkbZd*HcZqfO4zYWeNJ%$xPQ zoE~B62DC=5gcmWpY6i`etm(U*6u_EDTcW8CI4YKP@fS=H)oX3TU__i$e7eq61W=D> zY~cICj;&JA>+wgL>#vWgd9H^`R-Tm9LqWH1f|`#IERikmKCWv!-r|6DMX6T&mTB*Y1PLn@GOYt`9j& zs*S2xuU{IPzw%un^`q9@@Mi3Zu(2Z%|01w%)BxK&ZzH=UZuoc}hLT)7#9%|%T9Grd z!W8yUE!Swt5X&mHsn+btzFM%Nb1?=U$Zx9pRa_Z5v)Kk0pJ6;H>3tRfhA456*`i|4 zCMuErNOPR9&2s@mhvS!J9mU zg`k8_-sc9IqTaS6v6dVdDZ1Rkb0HijS;EmmWh2i4_o8~5a@`{h zdS79VdUoT=z4*q&qt&<7O#Ls49#0$zfZ2JLPa*6{zHP~aq&?l=RcAcWwoiQ$n6~`H z>pAMHQr(a8?BfZm74%D!+QaGXd6V)Om-*ae&2V}YaPl>(cRhJGMV%?62xlYVRMwgb z%@Dg%l`$NyQk|*Q(>|?B&(jyf?8TQ$!|? z;$`PS#m9zSGE=*&=G?(zk@yd2rA~#ui`|D=OCVM7^KY9XHVw_CxK zTrj>w69$xucM0;hq=8qd{wyt)M>ScCil7H#aZK@S1aaU9RhMSO3L*-eA{cNsPD(dF zhwxd7YYh=U;S07DUWyIq2`)7?yBKVB^@jEE##2YlA)Z%70d^C`RNPl0bE3B6c64{D zDe=oVoi+oDz`Tqnc|We$nRDERZ}qT$I3C`VDglEHCr;(2JUIE$Di7ABl!CWi zTBGV_Enb^e^)fO>;O@ld#cb4yk!|$o0>`iYw zZq@MWiB1F$qVD3F_48QuERDW`hae){Lflc8Hd~&de2~fCy_l`>bmf2ce5|)rSgX{X z(9Gb%*-z6|{Dhg4};t3p6=vv(or``0fiX=gR0ZY6v zFf39+-{a`L!@j}6Eco2{eJQRN?0q}anpbLA@$shSki4wohlSAWlZm$dKc5jCohjj` zcLZJ)*-~w>UXUKu`Np$<5?jjppWxC0D0)7%ZtC$zo-P6ci9W!9dxv!00Pa1l> zF`DY_8RzHwJp4=@vX0hKyox= zg;6zx^#WTfK$c0Wb zq%&tI%M2PE;UP-K^D3APTpIFOv}jtpp@uYn(~w=_i4RSX)QPD2F`On*CHVjQb$zG~ z**H?LGa7(1R$Im}OJrPfd zf~6*PbDWpm)&RS$0}d5LwXS=P;l_F`52vj(_t^;xv`U3V7ClRG)bfeC8G*j^<$3ND z47OsXt;EoU%_0k;mhRae2&Q^mDZtGK+8edq&iA7^sC@w;OLu};8=Y|-876dfZ@2a> zQ#;i|=-!h%f~M1K8oRmC10Kr+C?DDM8_hVF%d+fK*t;m7CDHfKNZRk_RIhC#@Ov!E zyu@0nc`+Ld)Nha~;IcP9mPD@^4w(##Tn!>#Jc{iulY^IjuGOoRu6Dtmb4tzhwzui+ z5aX!^jWCX>Y4}yfi0z^UX8I4JX34%ca8`e- zG>Cmzi|DfxhB@-Vg-b!i(3Q2QMfj{&J1m^cAe;%3Htd-r7~jZ?yTpkF)#oLBoG*QU8cTu&Ezbu@-yD(`X3%U2-fJ{7 zWqMtttMSVwlK;w;UP!498a?j9dqQ7W%G?NZvMOGRVMbDR1l_goZvex4JRE*!`1k|d zDj5l#WsxCTqOCsk!di8I{3J$Y_-X8zGv?=GH5|`grpOTo^d@BcZhIygz@Sfa_GjnXOuNO~>{mo8^6bK+PNYAa{+g^1 z3JO{SkGGM`zb;sha#u)i*d&nt2DuB14{{gnN0{D}53xuOm}6_Q?^-E+at1$aK#H;r zYTH`0bCMH1oHEO?L`_I8_=>I@MN2@UXD)Yl9$NTH*Ip&Bt$-9c;2|1jHt063R2T{# z_zwHs1snMW>TV-E;ndvaUbd%ZU)+)dM7>Y+?56y%9y6LE<}EtM37|m9uUn(-I$U?8 z#caR6mMLPYp=|0zo(EQKXPa7FqhgpjvD&d<;yW11i|(V_29RTMQy-|(B!^eML6#(F zTD)O=y*(^3b~f-$eH)>yfB%87kJj&zpS76HjvRNJUmg5hU)`IpV+w;ChtKQ?V(aFG zvmv}#{HOtOQ8>fYMuF+Q?Iw|Zf!AkVogy9?cpFkjD~*7TC30^22|8VSCZnYRL%BvpAdH+o^qY>hi|4kWjYJQ=4W&NzbtAsW@dQf!+pNtv-6u_Q7L%yeF>p2 zD9J9Dcl#s3$$Ij7 zgk!gZ^BR-A3~{A7$P27|n`XHkn`D$A*A0=w3(C7AZhq@bO(RZ_h-We@g^fP4?}Sc! zkNaolEcRl@4BL%U%$SShwTY(@KQ+~jVMB%><e+B|Z?%$;Yi^%z*|L~x#H<*{A`)_~DyJ6` zISraFSuWOM|OttA$?WP*fkKSg z*o}UZv_nCjs;7N5!-U0W{zi`F*Q=VNJ;wvXOb)TDk{NM{b6m4;4!2*di*-$l+*pMW z7A1??U0v-d{^hAlaD@7x5AGYTailraMn~Wd8}w2sAX|jSQ^ftD2^%(ySt~G8Ugvud zZ$m5)8(tgK{_4!pV*(9-gSeyR=gw4ja3L-SQx`x9;!X|t@`ECypM#Z)CvN8!=x>&9 zzD$e8+pA4NdUmdzSVV>_MAO=%tD~Q^yd+pC5p`plPJKyTDO#l-b;a<7+0CUjq}|Yd zJWS%Gkj1Nak7=>Mjl6OUVY!r#Jw6Jn(wgpdb+l5Y)$jJLcuTUmG4T7oFq$O;>dhZjBO2GlPgGxsk%d@cng7 zo~mMaVlug7r3S6E!k-y7bDTz`(ECr?a8Ja)L?)DpQOej#cGGdup0CUw?~m!HA0-5v zy4K6UR{s`xWb$|V!1QB%$_Xw)d*4B#Ze9ih2W$K7$xAt1WTrN5IIO5zCL3p^C7DO^ zSXfg9^ZRQl*G-9ro-60SX?R+@-Loj#$t377&Z9Xn!eV%YB%el)kDCN$q3fxM}wNR%i+56#-Ei@8X{xi^b!o3Ed7t}O~Ej&D>I&BM3 zDOFB;7S7zX;Sp(Ims(b`C0Jx(`D-%B|30{>q^*ca-K0hx>mfV|T~Hp$e=~!FMP#{j z>1Ml2b{MQ}f-PjGW@t=$tgMQ3W)ghIwaHQa!)A~>P@dlMJC==i@2JhVTMZ)}UR?EH zxwuk8Uho_Pjdv6zFOR=a^np5Zk$jV6QyQC8;tUH`_0Mdv#OzC{yO2^>$PUaCJfx6) z4WN*X`Lz^;Hg6A?t%hO}rhBT3%wre@W5I6AH5WR)4kwnEDd7Su`M@KzLIjJ>oMr1E zH(InH!ThXlH;@-OyBG}zHR*TtQJ)*7F1+T;n>iuN%(6Z4L&yB-(+V#|^(YeWE-1Rf$-j5YWgpSuDqp zb3EG?JgdG^8Edvg+%;LviW(Uu50QKQQ$iJ!*WeeEq822+yaM`7uiV;G6KKMvFk9|I zuMg3pe2O^#N~P1oC3hnYl^o;<(VT9MMIXnCMsIUAk56(vr_rKWdT<9a3Yq9y|Zn8pSE-?s^a-Sdca;&^ixDqjx?usl$me{%Y%WC!XbO_e+v^By&+>4LL;ZE^fg>|<%MG-dnmg`jt)@B*d%uEzL{SX~1> z?U{W<^M)hGk+bw%r?iDvii&Gt28$B*c$SJ2MTtgI*$Z9PjliE-eRfo)HO7SWJpr8; zBYX$}C-f;6vY_{XcXvxuxZ9g0_J1QhR>(qyvUe5mehc)zAh6ZWSw?&0Z3lLsb^ z2oD|3`Ob6B({aW7%Y~pbB>7ylO}?E=Vlo)w@#&_UYJrfc9rDaw4qU!vD}!VD_k3pG zBnxf7>1&dnN!CnpA#0}$s8nc86?r()k~OBqG=1Y#Llr_zV@1YAM`E#}OK4aVq0sd} znJak|X@{v){3-)qtvy8^KES4AOnn(E_C{l=RuBcTQ~BVPFT#z?-d z!5W_1I;&f@&oJ1X6*wKJKhr3ybG@ZqXEZ-)R{Vn`avpdBm@F~8f&m`C#Yr*bp7(!x zngqfOWYT~qNf2OF80t*%csq|`mPgTlOj6QK@KDn=t6V=wu0X{kVfMGyX`7hs7y@|p zA)*HCc)~o)7kE~BsB!8zY)O4V5HY2x2?z1+;rn<;=yIwH71rAzt+-Fsrsvf<`M-6G z%QS?wUGtckL)x9l2ZV39{x02~!Xt1aVtV*tIPL0w{ieVyjvmARM%#8S&N_4kp^$Pv zBH*vEh|x6hO3c7%%fK3Sd0tnS`0%#Ork@fTLyMvUM_~SPpj*^!Y*YD9=R|{UI+2Ye z+St*WE}i`J?PllRydo-I+2*!Q+rLv2mzAsBZoDwB*4~C>v_Rl$5ul#*mkYh4zwt|xZ4t8X0;X+ zx!*>z6rCB~vpo~F&=q2<-BE_%7dQ$=;o{Q7akdOmQS7X`CKkvz%L{a>%7oM0@lZWT z^#%LN$jiWO*1b#BAbV@RiM;(DGxVP1S|}ZMoQ{NkR-)jC-eFzD@`(<*jvRLGBkQU6 z&f-diLo3o3UzFIsC)z5V{S&qwF;qc!OHQmIKQ?e4rXr&Zx&9$0zaG=&Ea4dmBS31x zL`Skz{y1$eGfai*6U|%T$5*@f-fsxl{v$8+x*zPe9Dliqk;<)FvQnsY?AhA17T-)t zvlUh)KkLqD5c$7+0$_!L^U#6!{_{$__du0XXO*4pIUy^MYd(9(+C>|VPS;QZ<;kxb z@`)-(ul;dywqIHu^k{d$*Rs~@!y1%MnI{4S{VX{{#-;5GS>KSW&^HE%lxwyDi$hq$ zqGU$&{>j9ICg*gL_y%EZ5jHNNlzUGlSvtuJ5?nm`%InG){x%g*Q=8CrG*-jBFDXye zM@xPR!oTG_Dak-cLzBmRQCd}BlO#x(F1-e_!`oJrs`ylUul1l=`^~t=&**YrG zZn@nsLhM@fd6Ic(^;OQHI9K5tyvi9`-5^VVY_wjDsxemSuv#602p*fv+OF>MQ%teq z&G%C6X-AugrDOftcp!39OdAHRk z;2R+Y>a&pAxAq1L^=X*th2~jF-`|;!+v-oVo0MK?88V6TS8}g*Udky=)9^9S{M^ZM zkToSFioe+KOWz1&v(${w(mo9u??_)rXsAB4Eo<&V@hN*S~D1YwKOb`$Wm%f^yKVYK#J3@vePD-oGtNO zTS%h0GA^;fD95+X%QtEKVZ2e_Xs-TWx!s_6rb9Ssf{hV{srz6vEkC#1zaltO!RQCZ z?Y%0)NHSx{!C$Jfn<0{_)#7f4SuzyKD+LxJ2od86 zSou_QLHU$NOiSC75@F3IMq-m|n|9O6jiHcPw~H>?(`5)IY2=ILW4;hbbwI}B4dak1 zC%8uDpVZH_*Db5Oc`=(X>q_P)q)ix49%n$DDjUz(WO>!8rB!(r;qKOTQIV-pRuCV| zRbfGTExBA6oIb|A?&K1~NKUL~HzC+oe+3Dgi95{Ng1Vs(hA(T%Y`uQ^^2vp1bS=H5 z$>e;;c@5{)1!7kDWGR^e;Ou0g_fk&LMO$qg$vBxDRzJ*XOw$?5T1)y?{+nR#Cf9OC zKJ<_&4iSeCq<;R{h9CHI=)Y}vwExR*&YNkSs8#7Pyux{a>Y&R3mR<8{Ec^OZb8cc{ zlNo9u+j*=&2Bof_hqtOSjXHN;S>LdG44d0EBpvS~#jjQ_R zN9m6Tj596Rna2fk_%6x5+LIKvvyWy-uZ7{PE$`$4Y1+Imw3p?L^XxcbLsX%Nd-Cc1 ze12aAKk&NZuMU>~f)#R(Uu<-1`=asodpLXbosw_s+swGnEq&(hm;97b=qc>M4Oczu z56|0w|5=RS#|BrlGc~R|uxgnr^f`Mi0dM6Q8skG@mAyQUryD1wL^gSuZO=ghVzOgB^Srh%cdt(&CwTYD>=S5cU8tSmw^7W6wNT=XirVj&%9 zr17l<0z|wm>4d0OJB5-HDfay5)XwZ*<1W>(n}#<7#=z_jK5jHUJGbh&!ju_n`$DeG zag$bZ`8Tc(TGNiMjxfX}N1}cPYR{}0-)WA-MiT&uz~sC4i6+0`nld$|fzVTJDn44B z4|HwG_r8{sVer~VEZMx;lI|*&4ouexWx#y z+=AR8)5X`b+icyqEk~h}Um@*Jqq4O+)YUnxGoBmC%i~G1t(DJ=u>W{VZb_<6s%?0U?YhR`g$u}#X`fBoi zEsKjVyrW-rwtaCVK5xVSNNDWN*7+}d<{Uj!p7j$|@mBpjw+k)FdIQ4bsbQ_b@Z!=q z@4USk6c6nfyHHbz=F}v~izG^)IZ?+m?*F?;Ql*HKqRlBoXs=BbTvOcR zVGCQ_-0rh@uYJ6+v%TFE;^D~cRdIq9|3h{GVJMpnbQecAM=oIgiHkqP?$aDoiVG98 z(B-9C++Uu?Z}(37u>H{wa(%T@avy7d!C(=IrYXxV&yx@5*ce^{AYM+MlNlEy!gBaQ65B|LjJ1nu21k6yy@_`7Nkwt_)NLQ)PH9-UP5hJIY;8yM=bl%a zDkXEHwFk>m^Ni;hQ~A(4j)K{|)vBmP`W2PJCw^h;n98^CqtZ~~9gph|XN*E|GsmKn zW{zoYPou9qbh?2L4DeK4#J%XTyAec9QEc<#E{zi!%x(oX5ofR=W5eptZ{}fX#+R5( zc;A+R6$Cr zg(ZFyQxOz#&b8;8J0pPVHwO7Q(ygTz$+uH$oZ1q)<(f6ZUy?j!V)E#o_@5Ua*8KE% zZR!hv3dZ2It-{=KQW{I~ysDvl_D}?`A==LzHs=%SO`Bh*1-u05L#XUCyb_3guk(>9bP$S#JghUm-ji-y&G+?>hVMmQ3=Y=Z=v|Xy~UE&a~t(LQ=xBddW_Q^wk@%j8Q zqz`#_yU*($({k(3mn21JZupJBYL~+=*jj1Rd6FX<3<$5@vn>v}>XCF;4y9(s#Gk4s z^$oz{y|_lmq8Stg%<%x0|3lMy;O^~8U$}0_{SQ7~MFVU?^CjcJQsXVOwU(NUw&p|7 z3`+#_oqZoZiaYfl3|T`$%yA`bfFs`wbI-VC@TPVsQV90Wt*hOD0CWMpmAb{LYx>UN z_OLUS;dN~YhDB7YBhY*cYq~xv%IOnX*yJCb*DSuY2D??PJa*Nze!GTp9}E-))pVqq zhWt_4K2FUr`6ey`dO&;f=>XArMwO4p}Z}I=&(*}KHECpye9cRsseQN$hFtq z)lMok`x{V-#>x1KH*@}4Y*Y|yN-nE$(MLeg@ivO2w5Kf`V zNDUAtygn^XO!g!*-tCP6A+o=?GW8ykpe(-)85m|ub8*zxm^Lk+s&rj2Upn7W)|}Ut zdRnes?{0c5qF*@{G4Ci)Al7iNA`+Q=D1d#)HURtT)?MSdg|l7TN^1{7om}4*JOT<{ zZpOzE!)_hFoB+eXqyR&`Yn|=*?ls?Qyvbb^IP&JYm~AY-EeP1Ao1Dku zsptWpzDv7L6mgAHM#)x#7yHx(CNXZ_V@n-F>%K8c%D#Ru{CeM$a}9QA-WEg~a^I&P zhZJWGF@hI!p|0lKP5LBN`B}-;(h~iJI)^_QL-bt!lM?;$*Ufs)y>aZ0qP(`S0-$}S zI2F1&a8oew3&tCdm5;#}f?XF5{rdN=f90d-S|y_WEe>$pxqpfQHGCib^UA+8!B=-F?F6#?1GDNmhuN;U`hW&3Nwv+UD3!9p)Qu)Bag|VW%(Y*;Bi` zViauZ(!k|5OAPttp+6($f5^$ZOO=QuqY>!xSU2r?*KpdVCaBgFNIYqy)8dSi)EC{3 zIM0xtIP}HI3ecqa44u5(u`tih(%^lAZ z?)6P8egg>bORr>Y++38#U9kvCp{6@)KFGcNrPrhIo;JpCk_>L|^l$j6(?BTnZNSn{We>Ze}BBcYFid!(wSu8|i z5xFhG+EtAe5pCKcgDRRTLjY0Y)-`1NR+F_Kw3xm(tQZl`01hg06x3G8%L8dy#)_nG!<_byj;{ngU-?OeURq&^!u-?+eN_$erQ z{t$fCgY+PykUlIPRKd_R+r4YllSSu3-P4AuYAd1B=j1)rkqN^05%R7yLIN9r>^*I2 z9|ESpd*A!H3wR>CCu8HM(|JEMXXa$dhbJ%&yYI|~2szYzeP#yw7Hsltp5bV?i-2Lq zduB*VGk@Xf;UWD5XgO~p+2&7F=u__8+l_%%lnC3ZO=9bnrB|%~aqw$XPh54w^!6xW z#(QBE7duAjpGV*yptQA?1}{4|X>DRWwNh~ZGuT;n-DIp5o1{V}G}EoC&d%t=tz^@< zcDcTDf*alG?LqSU*$gm{X2aNy|LA^2ht^Olw}LiT?Rd2`8#c$IjN|I1bGZKM`?Swky@A#@@-`2QW|?A^ z9jmJ^#+5VAznGAHi5-|J)AvqMhh})qdLCf9uap|4=RXp*(znYk1K1nA`&j7j0f_M| zvP@!{5$)yi=z8lnm)cGZ@XF-9-@Lr|KCBzh1FZ)96n+}-GvTd{Vtw%D%=8%JaY;fA zx7O^r|M`_EvJU6n#t_D%49h=lI%<1Re;Hx5UB+jn(^3lh`_DUam-C``1)547R*?C> zNGAW2UV+XWpFaXVP2PvEy7gv^r)M{~SPK1|DU*3@m08{H$3)mSk*0Lv$9#U|CcR~v zCz1|*Q5}8yOZTUUm}vK+OXU0eBeqdEpJ#jGf2wa<9eI-TZg~CK{%6-}E?yqdEFT-% z4|QF)QpRmOQEi7X(pqcJN|x20#YU?=l16u<(J-9%*j_+&lrlI1DcJ)gm2^r7 zWb62Q8AUU>Tt+K1g-`+Ts(OO%O{;Lll#Gu|lhK8~<76o3v2@K+^$*hm_ z|K@}gt>lg>mz{Fc$8jOnD}uu?!y+dAghoG6ixsn4zxuA@`bLO{5BKh~dN0yi`qy7A z7KomRm6Q^9ht!wXawfW+S{E~@X-O>ou=mgT-hcF*3nfv!PG24I!;Rzl&=5yraAz4} zJfE>1%1up)aY*M3!Sj3uW2;k!prRz=ZGMOgtnEPDcvhr~Ho&$)3z+ok_faeA<55e# z1*S%aBFY0!hT*<$;MHw`+5{G;LsM?p-@F#EeKW@N12 zn{`2v(IhJ4@2f_fK8{1&n7Q5wm#o9rAgP@{l0|H))Rf;hc&Gr;zhzDM$7jg}87Lny{_^GGq=Kj| zm0hxcJo-hI>v!2!s59Z&*B&8FHq=~e_V4ucD|#iG@Q#p{5f~Fxz|g3TnruIF?R#Bd z4_@~tm1wBMv-O!V&RWCMj=S;0Ao!L$_2PMd03{nxbQ9d#@Vfx;Z2#UB|MAqvEr`~T z+i~>j&49@9y_gZ%YTWTgoUWr^T51AWoA0xwx)!#uBzP`mp3A}crSeIP5JUwBC zV-|nt-PCOi#m6M&TUI?#lrJQAcA>HRf4Pp5%*dRkd|q*e^q4uFu2TUm9+nvo{NfL? zRHAX3+k#MN15?}2eG(TzA5$T%Pz^8sXcLFvfNUp_nqsNz(|3onJ{mWjcB7u9>x>ch zd$%s@|R`goSuzPI|7ow`%FZH2^%D%mh+-z9=fITl&C()mFpB-^kCHh`HpBwnn<>{C*Ms0z7 zyNzO`v+?p$&AYGH@3QivcNuhBKu$=iYgzrlIAZY-4|h~AN8q)banGjpOJ{;s(0rv8 zDPt^9c(vgm>48Qre(&` zL*+B#5EMrN71jlkwCCIol9naNPFv}wnl8>?d@$3H&+gJ)beS|@PdZ3gUCut@_BwZA zS%up(L`wfyVDSRd6E`Ef`z$MHld{q8`3QS%XJ_ zXWAl;;TY%LdK*ZJS};Oy`krm-(?#He_k5RqW(y+ypxk}p+{)*L>t=kpo0sd{X4$2a zCj?dgyjPzhH$ePQ#>?+?m!EZ%dp{4-#(FyvH&wZ-mdizN-@iVL7F1)!#{ILP#%;u= zCCUszMOh$PjBL9V6QV7et_`U3(%L=Kw9DVdAQwdDo=rwe=Te=H`Z_bpHfTf zr+vGJobg&xRP;&+u29(92nfNIH&Yk}F+(%MH-?9*jgU?%e9-$BmmQ;E=!l?3Q#Hp0 z!F@Y;G1`$%O_2lY*xqjEwsz9$cY}MS(Wo`+FODIeX6OQr;hFYCbFN_T^7K57BCj2z zCb&KN*d+K7!L856m!yo45`l&BGh!jtQH_lbmBqioIEt zgWkVKI=Sc!sY^}3v5OCd5jd|6j!Yh~k@Du(R<2Ea3%I%K#Cpbw@o7F?iVP3r=6yU^ zy$KxpZh7@#wI%J1?{Elu`Bo@8IVK@Cbj^0rZsUC5RA)jof?lzhmqAqae7K;#TL)zW zer4Ml3&dkP+r+f8C<=h##paJalpxxS@OS%C7apkmi6=%tc1Bv@^~nCe^tM&V=to6}9#Dr6$fC4650xcZKm z!$em7b*`I&9=@cL@pE5yr^rb)?Yr&s+niafxD^0GKlQ^bie zfgj<|hXwfDrxl01UtBo3`cbxlZR=wz9wg~b^mm1Zk0&!cZ6cd&?pcfZhdraRdG)dl zbiF6ESbEW$JT(s6bh*Es@gT4zkHwI7Cx6>x#Xg zBwDb~Fmvo_cPk=M!;jjFU##fgm0?7)=+7R~g1S z)RKpOOzazD8TknvRs9l@(laWARo^oFed~HoVz1H)P&R9LJ3^k|s;FEUo-+n_HcX7{ zA~1@For`)Ji+?!u9|>0Pch3EVl31y8eW;$d$qS_h@iiHjTj)6{PyB98XVP?;{V=BN zJD1<=8R2S8q{}n9IFf~EVX?T4Nm`HvPfk&Y8c%aRW^P|qqU7@Tr{H+({1p9?>mTGN z3%`@RL0BAii9JN|Hh_&oVqEN|af^$2f_?GAD5sMsop75PiB%*!OJLB!i)!(K^haWb zSeGTMeJ6toOkjIpB*9$rc$412>F7EKL()~6S7jv0j1ihP;cJkIh!GyELnoeLdFa2^ zkiKg0Gx=Y7%%D2Kei=;0NJfXEgSR4LPAr-)ljHg{8pWiy{^&8FyET#rs(z1ldZX1V zNkwr-oySa+jg(K5xnojOiQU(s23iB$BFh}V>g3%mO?BP)Z3v&g_9ckN5&Jcwf9KPSx+;X(^@6qD?7HP0L7J_!MFH%%# zRupQEWKKD9!FjB>Tpfe=koyMnlsjm_Ix7!dO=!J#oaHOW7-3&@JO_*d{5(K^655m^ zjK6m+e>}Dpt?fhJTgVX++`2?2YzQd9O76>Cy4gOwnEw{}O$7bR#mT_pt{I!#qK1WS zF0kj7Kv=aDLV;n7U2&R!W|zv_DngC~y2Z=G4AbsM%u+&27D5X;xhmC%;MHZr1+}!evg-VYWeTq9%=r)P=qg&fEuz0G{`W>Ln^V_`Ilz`Z3!?`o%)^GWA%C3r_KmDDA!41aY(+fTu5vVl<_94}c_=Egtn z^8#1E4`pi9RS`4Uc9ZA*(DWrp)%9}}u}WTs2JIe?s^5X@v#5-YG_lzOqaWtx`~&p> zEBkLb=dMRv5#ussBV`ENXt=mgi1#&7^-n}PNhHtN<6Nz%13!``%!4v~++cfwjN4To zA0Yp5>C&}I8%j7UA%bgT%OJDsWE}?y?-bbl=&^od3qV%jhw~ zs~KJzo=1qJJ0=U@m8r-2OGTCG2ySp?-k~(X$rJ*)|6~>TJ6T@Njh>8pw0GA~@ns&m z+&e4y93vQGgbmMAhddtSKb-JAPN92d@hj7hC1R(olsmkLp^N{Z0H3TSuS1|h{zoPM zAq(Vvx|3uyN7R4bnk%HD>?BuLLwVR8=nN9^Ni}J6)4v=@vE=foB`=W`ql}NIzaZMc zC(;gP*NCUeEF>2~rGIl}y@KnLD-i$ftFeq{{v?^7ap?+WY>l>%dSz89$iHn$BxOCg z;AxXJw+dd5Lya(V2=oadL|az{FP+EoXffvzWY{dTcwafzD(Q~%^)jb``TFYOoKe|u z0_T2&%#`ZeNOEPEc1mg3&}Cy!XxT8=yNhv%3-3U99&o`XeS=mI+BvZrqmzgl>LA?aE*LQqyhXT(}p2!SlZ{}@C3)c)Ff_iI-Y?@Kd{ z%r7=ou!6us*KhwzK+bKAP*cRtQ7Q7hua>qTSsE$^JxC@h{+kwVK?2keBQ zz7D0|y+UcUZy|3opim-y=T`{x8yZfiaRZ}QpV2ad@JG};t zGw0NmIMKY(!2Yke@K-CLpoD{Fsv|((%5WtMsRREojbm*V2=mlM(2V8%DcSe;i(;>% zK3Xy07AWEilbHMYmVoq>hM040s4-_GmxgM1>>H$ALU+X#J)01`1|2~!84FLV5A-$^ zu;rUH+&sP?B$~k4;H5ZXD6S{x+w>UWb>5oA-os7Xqpfu6wrEj~Aw^d)Xc*B%9kghU zCQ}f(j5?p_bD%*hlAc*XckWU&Au+@j{Gl@MF5{}S1q6Zp!ZHh)X4RKRK9)6a)Y6M5 zc~23YxRU;pO%YT^_z#Bb#VNMXA8DsuWH$W#w@=Ewt#2qk%P#&{@l-?aokXUEvHnC7 z`1**?*Mv8H$7wB&kC^=XNb2vNHl_PTUz3_9jMt)ux^*Ilz86Zya-CzZI8j>Y5p~b+ z$`2c>YxlMt5Tv^pf$y}^K}Z2AsZI zyD_nX;9w}cyt;}Is&l4)(CI*kuY5_=;z|zNb5L;MnLHI3{me0b*x7%a!craTIDs5V z|BUm8RQfz~<)5WlpD14cTW8S@%Bv-dGucJHGc^~@xk4J;&N$|oZjx{Rb<|u~A~q0i zVot5!CgoxeF30SErjks!7k8gTJ&1`3sX`w#b!9Y_%No08ULD`bHJa8WgQgZ;iUZ!H zGO`K;kKNTjIdS$$AGxK4vcGMsx)U%4qQ9VCx6-OS21OL5E&8fwo{3R*`>~paG-j1v zrx8T-SpR?){w`;(B$={;_npGQ7+z9}^RAy%iGTH$r5{4p-|(6OG<}X7&xipOgm=e2 zu!^Uj-PDh~Qv9z;$$t|_mIlA^++%nSK}8VhvOjWAv7a1zFq)Q|2Vvb>`eSX0BE zIxaCnD~PZIc^|v~^*3TMHcbEb!cC8U`)ASC^*tY`C~r1A+iO&7|1S+L?^n5h9K}Wj z-mRpXxFe`=3m_fZN#=%%y({_X1i11639(@;+@)SnZ%g5*1-fy=X>G17&*W`16DCnR z_mR{bpp??VCbu+Kr5#*_9=8N(oJ=kauX=9|&G7kp{uWz5uD^~;+^P6|^hog8;g22t z$h7JRQkNt^srwzxyBywsb-o_f%*`#{P4jXmioii}(WUGrc;Y-j4!@Ju>SvWf?cUBL z=T`pDpt*17?GMmrF1j?n_SlUl)&`a58e1`oS*r zU93k)wgs@~nzyn~F~-G+SKd;;k-Na39-V1h4_7EQsQU23 zJER$`+eV2}<@jXMfA8a;%)7OFqNA~^RJe94kc-QzLw6)I_1VTmWtd^_Oti8P6wi(b zFNqr8!k248V-Djzp41>cPyProjXg#X_o*QdcbhAzu-085C(CW#9bCAjMT;TV8 zJVuVN-Kmf5keGQNn`{0!1-0%0mMtE4=261uM|1HJF>b-}jNqy0{dR&`ZNOai2e(zfb?_&FE6(b`ZxW|9rMloE&asL^Aw`|+NBG4;uQ;K_RsL=(BlLqg@P+$fB3VzBh%D@aIXjyRsLzygZ# zX$49Bten*7tq0Q*io$HdwED5m&J0x9r-)h zvgN=5FlYtX+{v*#PcEtmBJ>^%?&x?hzAS};zg;%^rvbD8Q^|P`gZ}yX*t6+Cli#r6 zox^`E=xKpIVKvWP$fVWTfi-m?Y^g%L(5({zz-!4>4zdlN2{!gG>3apG|1QnbnibDk ze47@l<`>y44=z7BLQ5d`6Xr@^FtJCi89<)~hPV>j%Hp)stCB4eg;L(V+SOLu{(iszyk47LExyn9c|OPc^Zp#q5T?2D zBIm8&p^FJacY)J;6gL0jRIP@wWVY+QCN|gU1E0hmx7n~741Li5FI%^`-YwzBnTR}l zIg|KdO_>PEsWI64!!&f2<#u5@3u3+?t;B{MsHhb97Mgda8EJIfAuWhIRR zC22(w=2taFhc1c@KBUCw2baYw5`m&HPPlXwZFSp{+#}OZ4b>Fyr(SRkGA2mMG*XgglN{T((CK9YA81IP}DY|S(WL5 zt(l$1;K_MXoEKG?KS6fJf2oK4t_@a`BLr*>61U&}O#l9YI1AFTvw9}ON6PFT7)t2b z3|(|R)9RSuoOYgg4%P|td7#ECdj6R$eeR&JR+r351 zpn&>;Wy^j>ly4O$n)F1yh1vxZmnn$6Txlb6Bc&}aR$ij0`)dkI@ni+%^9j$XIlr{( z!Zi2Lmj1@j`#3(bedyGySlUd2zbl0{A~^y>cVgL+-YRIrQ9HG)5#oVPC|&>|ZhiKj zGPp=)^2cvy9f}s$A43_|g`VQy`heed*N2%$&_}9DL!a&{>Ya2e9RV-#5U>4OGmeVM znX+IMg_b$#5?p;UiGi->wDUw;_JO#z;FK_vp23;aQxHOW(;?PI8+V{pQ%)UZ6uT}D zc8$(Vtnsq{7+h$K?KF`xCOuJRXqJuc*#X-(iT^<1X(RKTU@LC5HLg|L8`9!)KqRE-JGK!bV3jV}! zoo6ma>+JW>V?5FXFPTelW1}h<&S7>e(P)Be5i6e9n0oNK`C!kZj}ZAqWj-;w2Vc3T zVkWI;a!E}G+W*D;;EMvkFO23_+TOn#6>!^@nG{tG5(KuGn1@t7J&5l9S-aUe#o&~N z$25LO-Kp(E8|{3(TaN|mwKt8t%jw!dhE`^;KGn;n%iQr=)m7%T_qrt=Zo0AO zz@Xqlk>;%8l`zf!KWQBw*NoNMN9U|MOM}CIS}p$4R}i~x^;Vr*8lmE4V&P_+keiOp z+`9>~R^@B%D7id%b$iGDQ&ghV*!$O^!VZu2IuuQw%4ap0c8|9>hAT73`Y@heI=_eL z(M9uMtY~#@^YVJr=px)|xIQkp-mA9=7hT|pzACB}?9QskRPbZWRuKbBTHqZ9Gv)Q0Rl$&F<@n0LtyK-=Q|GMn+FI!e}U5Ncp1887ZH)3sTL+%0; zojzZ!E?8C~XdxMHe*Vs3qKcwqnYqVcj;z-Q+#NZ)uvbya7-YI~Wa&jEo{{n1YKijRa~MSS5N zy6=k)TqaYWUewQh$Eh(;wtGmhyV(PK>pr_^OYM+{#;Lb+nXkNut?rD~V=QMIEeOKJ zlM!!62OGP`8ATgiy?iddoqFc$G#4ybzCTzxgru5lR5Tk}G8z;uebaGi5i3h(EZW|=PKtr{E_qGL#lCZw5zfkNngbk5WBWh`13JE6Ff0W zn)sk<55n>kvqe$cAt~N#2)-C`Q3bwX!{kfzbtdY6{1Y($voV;LOATdnUrLhRIumQQ zn!rqKoeDWzJSTRC^|->RbwBClTM|V=-m}vnf3m%?E0;l*cJ*eg$nWvveJa~}IrsoK zrBtv&-aAu4uL{LKnjIrJZS!-4%9!e8E~buGG-ayFi>}I;s(w0*9CMdGUQE3BybHEyaq{&L?8!$yYUj2fGcN+zTZ9flM3DEp zY0XJ>IN6(!*o?e^45l*u#$YNa!jLdLt#4RWTXQ$8?F>I9KR1AuXGn^5W%CJK5i3|G zYP=@hlbvI^S4NU9wW7kUFD4EX&cw-~r|?E~ytzSwESyDwZY!JeWCzozyPkQ;IisG* ze0%Dxg~^C6RZ)`84L)&O!KrJGpAV4fKwaqDcBh_ds;=8<&%0qR1R2Km(dERLy7{U= zukU|s&%B38uP z51=mB87y;k-3pvVqXE;~#N;Q^v6?_0yn!aZ-4<_B!wan=(aI@ww4sT%Zm@xu93}jj zbHf-WHE~%?Z^VwGho6l;@h5RTh>OSUk$I~e$H0G~H4^yadu56Ou-TflC$M)T^-Soj{$qWCOlwklRZ{88%n0Z} zXGWwZ_n0el;WEE2nDqM6LdOqsFf=D><7c+xB5-Wb^mhF}?%r!d>0U8Rbs^Y`rd|oW z-bi}01pa-1Pzs&CW4fM+<)hj=c*;?xuso!T^}uGB?|dQu`OLc!el?d{%)Un*xUSYx4cSUW5kd70s(4fTrbzsh&Dz!%t}`ZgJ&D zn^#Mlse|m?%?u!FWJM6u-zD|jI1gLX=PO1r(xVn`h30~<2@tHLrt>poj)#&G%V2>w zv73ZB^GAJu|Hu4Mu%c^0N*<*2QY0EnVHq@GFh!!CI6b3VTIsQZGW2?o4sb+^ZNM7w z&vt;SOD}h(N#F~PntSr2rC!?T>1a7aqO3v;w5g-C?K^`x98#$>Tn-mRbw-T% z8s_2sDRi%~oKahR7EX?0q!El*R`st~RyYJT{6|z6kPOD^9w(XtU!DiJpAX8X2dWpF z9&M&kTRq2KJI$~fE8gZiTQ#H_x67G1zm{v4 z(IbSm`R=v|;PGkACwL2}x2byFZX)5{cSB1O6 zOdW2vI239Rk;ioMr-lljT_Or}r^bNro)TbQBc3EDTZ^I5%g}=8tx|k_iZr+#yj{`4 zwbi9vt~aMwj-Er8>&vm#)2;9~tcgor7W3nmQ({N>KRefL*eb0|gK=`{BCaF2PpHD= zkHCO4^MiCZ{WVB;G1rj;#m=vis~AhY8E-nf_ztd1R?#_ZV5i4*5d~MGhzbL* z$owGO^{22y$Yw`L#lFP5R-LOTLvJ!y`OKBbT%9t+i=f{hfAWwe!ZaJFIOw=JJWG6i z!#v3hrEKO-IO9U^&Kyw>?Lu{mzx0XpD4MQq>bcUG-1uQKROcs>IpZRYm$DV_IH0Z`}n*!9+l~dL|G{KM5gXJ9(zWK{!7z`(Xz}=15CvYyTtNg z`c6xdah6qa|NHX2WA7_Aj*<8RoeXPg7q=di+SYrgmAbYiDc*Hrh5S{qz3A|no>v5l zwZ+!Wr(WG^O^=dLHkVV+jq>z8bABWt^NH{3?m)q7p*}0p2iI4MZ1(YLUOU>eHChN; zNLxY?#OsErqZ!!my}Gd`9!ZXGll@n(58n{srlDJLy1?n}`gN|CrlVKu@_?HOJk3w> zQRzQ}xVt+J!S@pYLX{LOd0&YVWM*$`>@#3qOHXvo2Jc4g?v@x_KSqDqf&E-9KitHUSa-uLrcrUy%7A z4WP&?0~3HHiQxT`P;YGZlFUVtl$GJ1eh zL1tw7Z`K?)0qzNBRRF=mGHO2rNn!6zC;`Okdgg!|4FZ}?+4an&7W!UtX!E_pV_Tzs zNl#2d@gA*NQuTUN)^L9hpiV?|)l?y+ePqk0!z&nli}?~T`A2i+)*@1y5o?S%rnn{v zypQ+jeSSa~Rr4slThPC=-^~~%8r&H*IsxHk4CYPJF%Z?z< zOBHfln*-pu^gbRJvKkmKh8)xLzmF*vMfDOx0+r89%AE;g&Tui1rT|2mtaAg1=_27~ za89>t3GX{ndIx2@BSt0gQ?G^!1F+#GBcv?c{q#SM~ECBg*EdhwFjtro5*hqzll&dPO+}Q%@LjN2-iF=13nUiA>?QOt9 zn13gXic3&1-!Y^M7LB9OjY(0}*04i1I+(~>LJy8x34(G=A~<}I@Gc4bpk(h_Q*Ejy z9=0UxN0u2PP)jLc_FKa_;@vsF%yxhv>>EH-INbOsU7Tf(UPPp-dNgV@!2KNUBowDC z?an80Vou&OdN+eO^RoACky*W`0vVMl0aaZ;%g}A;hmk#__2?>zne827R4Sx&0O(j8 zg~)p?9l;+_=|)Muk7iBOsqx0zj?_C@&+L9LgfqLlhi7OtK;Pbj?rT%-IU@xT@Y%2) zp&=e<`l6A<87}KPVzepa5;5#`c6?IzP5>2DT~&kGzU*47sI1DBm|IgF+;-`)c~f;Z zvYsEVMWpoF*YRy%<+4PDGtpf?(nj^o_nxb_Z3S7Ipvv5KNA}oSrB@5n=a?f3Zt~nyEIUjfy6bBWM-}kmDOQtQ{NL$+bq~3t0yZ zWN-U-5%0wr)*!-SrC`2M2NM8Goc)l!^C^E<2yBgVem01{3eXPLUof>^K0$j?RTSL>Re6=m!N61uL#+ZX6UjFaa0>}rk zEo*f!f7TK@a3u=niaMAZwS+F@UytixB3iuqvX7**cHkfzFeESz)vy>M|!pfx&EQ zpzDzC7X6MIMZMy|gVli{R&XvH8J08Kp@X?z%io0)Q#9Ap!GzWFzem>TUI`RR?A`Rk zNhNvYv{>ue^m1D~enoBsO;kUrLRvrl=AgL6c2hH_J7;#C3-P318X0vwNey+01@9tC zhiwFS&`!y!XL6W6^0j)T!;8~caf5F@={clFoYF+NiO=FH*^w+xK@xC~Wg%jeJxW7Y z<48FsQE+TCCiedbV4^e6J2Bq?W%MXO+K>RG?&LlU{3By%P*k04Xu4F{UxIF|`S_ zaQRxN*T>f4y6a_!Gs`l|;sc*~3`akqOeAGde_{mDHcRd5L)UsFv5zpskf^xef49ig z_$1V0y5?B~6i$XBxZuyJlv*7=%(baT=SqSM_BWl~Wb#ZI+)v_6XSQ#hgM!o zgRr!>J13sa<_C*uR|WePfw#V8Slcq$w>@*P3$B6Dk|wN>j{Frta&u?j-yRm7X(~&) z!amH1N=rm2-Re)M4l!mIJm;MwF8DiUOvegFAWUipS{3~AcKv#v*lBuiXt#`xqW0{2 z=FaZ9AjLzooo~mw5n#!&BDZP@-AgT;?H|=drYq#Nxd6ynd8hc41`NhQpG=`676@6t zUA0}a^ZPSvciSIyOIrw0pL&cidWc&Q1#3sw?2DuAq`2bwSlAaBFfV8>+?V@~%haRT z`69K8-Y|TYY4CM9sjY;8znj*|JD8oGo*0om{`80Z;itS-9nbw`ZECjUGi$W0M!U+K z>J2buIgMD<&Qq^$G-X*=kln|+UI;thGIhvTcPO4YpWmNe*hTahx(R`Y3~p|QU9p5u zVT&_mSrKxolD}pG}QjtzDg6y@ z>VEdM9s;vt+W?G#2=_{F;Sh-nmG$-njQg^iZuFVK^zvFvT=Ep!WtrD{K6KUQgD)Y&h4K?|`cuTEPEXdhZrfTkK0ES! zvyIb>wbn0h?>N~tkVOrHTOB%40onz{DkF8Taf5vSEUJ9?BM8w;= z1>w^UiMm_54dVHj*j{wCM_l8h5&=_=n$Y%B!tuH+TC>+U5j=hYQW}SRIQT`Rg9Fbm)RQa>10%+Joaj51vgr%z(sQ}RRmYH7rG5J zNg5<=k{-!`Wc+uehKNs%M2lg8OH3>J5)CvWM*B&3pU(%CR1UV#`F%UJ&t~ zXm=WTSX%zm*2oQTDQ7^W_XoODgR!*>PJ;?oiIYvMwyOytV&kwW*evYS#bu%#do(ZZ z$aFKWa$c_H3HQKul>48pf>wuqI!;A+(97ptRv4| z1x_QeQwu@E1Tf^xW^fCQo?SN&l|NawUIek=`>!tliE)#LqFCb@yWjk=1VQWy3tb{^ z6U?!wo*#8k!2gZYRVBqImB>on7SqC;96%_RG)SDC5dnn~ zaeaHvsZQ?@5UyVAR(bN)!ol3T1CKhdAK!j1P%8Jvq9scaBpkDNVe}9sJ}3c$`=H;x z^%{c1cm2G82w#PK=Prw!5TXDAgiEq4HFSAxO=@bNMhG+pKphO>z zd*gz`_ODcoKc(SFVKpFoueSIz6%5Udmj^9GritQqB3yfo+Cl?^Y)_RZ3pxZr0PubH zkKY_j&8p-Hw2v~Y+6e-zRzg7V1=o>1pS^)*1-_og;{%#;?%#ebK@;CkDlR%h9(i#b z-on+SMp(ryWBGbj6&Ts+_U@w7^HqmlJ}ebAm8M65TKG!P0oEX92XBuLf>%D#p?7E< zf4VrJF|G>(3r}**Ma&!Ic9ocxZ`hRgW14u2$#P7&80Gcu7D>ssgBj0OLJKj}ORNiH zuG}*{3~8)e`Z_;hA`tuW?8IHAo=9iQfj1AGfE64+2Y0nv2~2Ol5x{<964C?Njsc|( zF*LgmA6Lt_J>Gs*x z2Q$HwlpC7bi@`;)Q3IJ3(wU{A9xuU1es1J|Z``^y3ReR`a!DXw9NK!Z=cKn!=x^X> zJk+2mKBwwY6|q&yo!53gUEEQ&HCl3OVlGU}7Y)`;3v%A9b7J1cY2z zd!-cm&~*ksZAXqATGf$-bL=#HsX~9leAqqL!uM5J%sLtU)^hgQ3ae1<-8aGA$V$=& zvukzQ1I|$>luSm^)Kv}(fkdir&d5>R^klS+si@i#ZXcfb4z!_IuyscX*-cTGK2IlV zUW_{wypq@d&^ni6Mn~#mV+92KR<08g_%*N1dn%(UIfc*m$xUR-R2NDeLf9jd-oEBS zhC$TQ@`z_Lo-CF9<1ts$i9jh!uy!}sxxUrAx_s@XXMTKqoCpC2C{dd+(HYFAb;jgW z?a{mGs!3vbJ5Ih}ZZ9%kTZ!s-?^aqpfAAAKpoM}+boK-^xA6!`8zQ`YvDhquH95)2 zzUorawiopRubG^#t6!G$E?Eq1PN*6}A=|3CP*TBG5AFTB;WK#_aMEN`t?Zrj=|t3H zo3b;VLGmjh*5vQtFa-|mR#ygukYNqkk<)r7aNx@;`VZ*-^)eIL^ePjO!#u)-G`U*iAr8xx*`M& zt&-#}+lP9**G+It;OCq{gpo!LL?Sq{3||X?sQRk=TLA{LTUKgbj?2XFUO_##2eSvV zfE<5ac4}fm&gSb1nSBiqo6pe!i$5hq9=uvL~MZd6u6N^zKCU7%S?+?IKRZura1dwx=7QZ$GO8QoXG4@vGdFCzqG`p?JkX z!cUxZ;)fq`XCodP;kV$!K^q$w5H73}Qut#X8%W9x@D5+~B_<;woFK6~OIyBmmt-Fz z8kwEfm9M6}G}dz@QN}|MM`>nLX;@!d-$Tx%!ByoQ0xSqb!Dt|Bw%n^*f~f`}P9rAu zoak0TA3|f$)~FsjdXB-`2KG_jYa`P*QP6`#`oa&f); zhBrwASF&B|E{C!+*%t@}LF$xs=ilr-53cJhT-uTWORm7@X3`J~uxuUfNPep8s~x2; zll9`t5o^NW_<5XWpbJd$Op5Q}XWkOsY+P0ATovw<3 zfx}R;L|E!lve(=_18P1HJiKk8;kaQ9sSb&Iso(~ckVH@X#f7O=4i?Jku*x)1N8Iw^ z+_z2@XT=mv4P>y0VM~QU^)>69UT$=nX!r9&H5ltLx`=oCkG+tcqaVzD`M(WC_@xydmlqE|FK>`lNE zPaW6`Sf_8H2kEMx@3Z1Vf#bfh@*7q;*iDbhV{KAb`%6SxavtRTuA*MjHJxDclP3sl z!63k__Htaoz9@dyb%l-}702J4?7p&E=SHZG(sr|Uoj@EUO?bg9muSKOGbS!Ul~X^8 zRR1BI?2jxFmYGYp3ZjP3E8KJOFq(@A`OW0)OIUJzPpdWf3jox}7SD%%V ziDcUY&KG31-cKS53D6Osp`nXFj1w)TTKEE8s8tC*4ml7`Mi0hNa^#~i>U{HkvJ(*- zm=tzwGSC0Q_hX`$eJhSqb#k{b>zlnoMDmgr{yV*NV|9Qvw3)+JlM=V#$KBw3)X ziBWBbNoHyuROg0c>a+M+oJe)3kc32{P!Ex<;99;t>56&*TPSF5)wn8 zYas4pL@F-XNY``&ng_};srtHTIY~PGA$a586}xWif9m|}juQvmbiimK_7qe`=nRXT z-L2Byx}S?HLulb4>%>@*fs^C-g1z6coIYX|rzMOVvf%PJu;AK`U~+D4+<)~-$s@1v zrb8>0dN(LSoZj{3WH4d1uF4L{nw-q|By}~2S$44A;8)^57BS<~@s(i4?fk>XQg0tp z1bg!Vc`ST5>6+eMBlRpY1E|09ZycfOmI|8@s6A}XPR{$tL;Wm3+ui2r+yp4xsw!r$ ztlRJ(YgIknjBX(E)&dmSNw^kt;R5hO_!U4IbVC#zv5;d|OX5I`egOL*S6_n|H)KX) zO)``75Y7-)4W!zOOfVW{AnI0r>(1XWcWpJexiDZL)FbCqo`%Outy}CA#dx6R_E;VR$`tLu5%qafoft$V& zHh=BqEa2v}QmIaNBlZ93`^}Gq$gb)cg!8lfADo}hNVyvkj9)&27MlJC2VsG8A@71L z<8GwFMD6_7&E|$;M3^sRFBIvnivlCK5)~3$+YIN__di%GaSNuA<4__%1iyw4x;WIz z4S@xUaDiL{7;2o%fMtC6??(aR{jDJUB?%Znpk-u{r@UJzod5bmr*1K`e@CG3g^qEshGQ^#JlUj1xj!`v*~%k-EL0^>4feNmA%T0hK}vkySf#R`7zL6I z`YRRQUC8(iiAHu&;*)~Y3~D%l2uvgdXmB0cLVo~j>lOXh!KTJrBk3{6%PV#p#l`1A zXiUN^4#CsM(yRc}9CBiD(=e6zz0xq}3 zp{At;H8q$QErRd8hw#v4oU{l;rAEmdTJutrEs)4lHPFzM^JB8F8ah$nhn#IdR-uzj z#L$JRjAqcA-3t%0`8do&g@Sc*>Y?KxC*$A1?vnyCbT7J=1&EcfviT|*Eu`)m68YJ~NhN!vxNMOHnk=uo6eGY5 z;*>Itq-EzgjC=nlE(fW0IV~sk&b>q>Sl|QM3rzplJ;af_Gej;mpI^U{T6IDSIupXN z5}{jl!&1@Dm#3H z+cv9hHCHd+h^#3&ULMI9cZFzmjaJC;Y2h2qoP!{(Ykul=gqU+rCE>3wN04gh;6hS< z(tXD5I+&uJAm43510In2xD>65WheaeFmirN?uqB#C-n;yvE4WXjd^NY7aF%JO=AeS z>?xeXcu0+o`*`>ALU#VA2Ws}?9-}GC1ed7Ixg~cs_N8cb$y@Mxdikr21SO1i<%N~& z^T~uJ6&D^R&dfTw5BXJePp&835L|D*T?Sor6gx|oaPPKG>8iINZzs*N?~XRl{V>Z; ztLiU4T$;5#cX5UAq#}x>iafe> z>m}!U=LY9F7DlUa6*km0jjQU!wTnlN-C|z&#UlzFBv+LRt&NrpsiO;qqvdV{PK^E9 zsx$H9RhtV_fbEz0V|GuWN5tKisI}bE!lwk{bv;k|t4wN3(WMAU#aV}mleE!{E$r!F z!z&4jQdAp%oeG@EgMR(GKmtpGf4rlOpGyF|^d0 zFfDww3n)O9#ji{5C4&NpjVmEsON=|Yn4-5940;;$P(a~aaM0aP_g*jDdp^Sp%>(S` zce_?_*>^%R9E!+2UPIih!tUs#2jcm~c<}Ae!n4;g2wKdbVEG-n2(B-xdEf)BFB@e3 zerEA3b|*ZRYuBG_J#grJYAKf6=_{aTs1R0DWwzTPAhGKg=sNjjVX{rtXb~c$jv?|~ z*`aCspa@#3*uX@ak-t9uMvsdeB{?}Mp}HG}mxn z-@Qc@!j7K<0zyQOoFI+SemIG&qiI-G(+jZ&3A@xe)o(FgXWp*)cvba6Me)p7 zTz9H?D{YG-*Xa}|4M!I}64e&ZCj)2TF?$I0)LTH3fFC6SEBX2u8( z$V>LloK=5xscmQ5itBTO&n56C;@Kfs+$@-L=GGT#+axscYW|c;kJZ}+g_PBy3xSe#KPFeKH+5@9JR%{F?vyg2I2X=SeLExi(+CY8LHDv$Vmdu0?HjH&|DVlfiE4+BImO#iX`N-V&p zTW|Ww@=t(e1usYrK8g&(_z=iHkJK76>(G3y0Riu!S7v@Dm=ydb@>^7sU)h4mx&m zWLP_{z5s4oPD@T^M;%301z8V2)YB7D=?|=}C}*uG$r(vvDkl@jHw4B~BL!7-)?1{- zB&k2ih=Gb&E~kKR3J)v{rW$Bh-!g;hTjPb56Hk|+5S!JZJ-YOQ_swm6zQAAWF@*hN zX?fvgwT5+5V5NX#2UaaL!}>&mN^KH67t4;DsyK?YqS11=)b8$Bx=#=Ah~|I`?d~7T z;aMAmX@~C5hj7pv^JsfS2-3u=wJ>2k@dtPEYj0h$292xA5wfdBdNO9(BuC_>?cmC^ zG`KTsygY$&rH9~c5$G=A7OAQ#*~>j;niZ3`ORtL8XfM!e7b3sC{Roi!{>HZ*D3Q-g zh6~y>OY)BzI~v0bdN*Unl-_X}!)9Irulp&N9VD|#{U@{8KN!Jn=RA0>QUCwzCR>T2 z`DOaJHPtC<`N@rWsY&X}QvC#85B@j1yPdGvcp^U&Hz&u7FIV>A>RtA?$!3fHijXTW zug=>g_-n7|ccq7LG@)2Vf39Jr z$EN1SJ||IbG*{H+Zyw+2rKsK#8{^=ofCVQ{eAf-8ALxEmuZ|{c>o4K;D1%3xXba{a zTaG;A&u>5D7}U5T!TYv;;NVw(eetH4Yz?IqVbqm76B~{xIX=sf@RM|I#cwmocg zcf7O%mVM!j+xi5EnYeas($b(JQjFKt#(iq>g_Tdgxx_jSUldjPJyQzS$NJm5*w?)P^)^%gZ@*0FCNwM}Rq-44 zd>0o$l3U>0JGJ%!)C544quShe4JU|}@xPdgnoKM?193@QU4h@#TdoV0-`?^-&+aDdE0tLgVJ*cW?!(J&uJ+uWgEJcMvXRog#G zJ;I6}|I6Ay*%mZc@CYmblrR*L_cL8rLmiXowJDk#3vVrjwcvQZ>TEyuk^3&d#b4}n z6%R?;Q(r>8xCad{7^D9YYQisFfEG3(bjFo6k_+(Vm+y8e1(Fjb9wH{2jc;3u?T231 z(s`J;AqbdQKMrM8`9FNkEa(WO#;U6_9-c0;G%EX<>%xnXc`Tf)Uf~WGpy?k(xYNqN zLze%vdpqe8_GNAf0JjH2mm|5*Dus_5n6pI-k8r=e3s62M-4p-Ef&d6MD(kn;5YH}s zS(B>TeOTEwCC$%J+D^dFofi2BPHiRlnH@M`9uL^FrGJ~Xk2g&&0D)hK-_nQ@(;d3S zIvS)oH!%*n{R{Eh*4$#g`q<{hHTf^w>bg~-y}$Sd6+Qh}==~n1*@Ho5|HxonSle__ zp}qV7w6EcIe`XLap3*p|B=nXAee@|uf-o;qo+HNjrQpB3h4S9!#ZX^J z9?b3)Xwxx}C{Aci!g!D6>X+FIQ$+dObP`&(!v1=Gj%r>XNm<_TAoF&5O;SpU`&1)I zF7t#1ioLF}ymL$3)L3rCgNDgIIrm`jI@>;K&9M+GespU?3&gSix*&*H{%NU%5`0rF zLrP?47?vGvRLx*I;kksEmSP%V>iXD_23*5&ovmW2F{-F#+vtT{z4B2hlA&wNpaaQB zE{s4CBK^nhMhnm^yq@IeuNO2LU|MER-WL&86w;EQ=eSl%+a{GTwcEJDq3l?JW6*%U z{-97FMJ6Y}2hKy{qcDmhHa+I{-*)EXD2)Yx{Yx)%`U<9KEWj9BmO@;NNz1$~@LGaU zZ@Z2JPqPWuqbmr_5c3!Z_31(nqMXyl(s1eUK&6ilBua{*k^9m&ME}b(hkh7akFT6j zdQdxc{%NC6yEm9Job=}+bZ|?BF+tP#wFKS0`efg1XiHyXM-Qgo4I7c;*xkA$&~ z9MAV#ACrt(Hnfh5tK@TVaYTlsW|(%1a5(40?NViIgxBeFsfNa|^@aoi`_3+_r}16x){559GH~r9$sjP2DVhQq`zrT;+5AdnO+1Uk6z=#u zyGjw}Rt(*B z-GfF5;BDfcOG9d(RFr$kLz5EK+A_G2$%bTF@9a&ZjOV5#*0Z@`?CXnpM}c@UzQ0Y& z8n_mz@c@#$EBM>&I-26K0t8(R4XMX254LS?Bqt(*rEoKR(#Y{?Raa z`qvQ>?q9XHQ$LJ93Fr$t8DSTs407hsmcWl?uVSNIR-3L~7NuH+3Lx!f9yrXGC3(B$NiCayiuBxG8ZS9;7N?_42y6^7%fRI}}E256n2n%I|(B3~O7XG2==V^w|Pt)eZ#dEU5 zlka@yqTjVqxo3a-*+Lr{W5rVA*}gKKi+Y8ydr3mVzk7s|rz-A^VamP7Zq(nAM=~KX zElq9bY0I{8DBF25=287_IQBfVtM99(`ditC!{sN9D=a`4RS24nVTNn^Bv}K6hE3_j zctyWcD!PdTw#eVY_y^MPLxTKj2Us6Y!_Q66aOO&$0l{eo<&sb1Hahp5_l_92{+w^x zU^r(F@3h8nF>Bf$>b3e7aTPqD80l2^f*zY63NB%qQ=V($4NtMe-Pz%nd(v)rz2B6w z6Uls4v2L4P{hK{{RKJa$({NC*wU-8~?XbtdH8_QuwuBeESOTRH6vio}I3!411K0i*oWVA*v zo9lD;gGnQEFopqBSDQ)XhpQO4A(@x_svPeQ1@*feeFx;M-L;r$QsymU)&?%~eYP`nG@! zIHI!KVSZ#*$Xp0*_9Er-oT%uu@|NGWO|&h!?6d5Lh85G+YE&S}WQGNwu;(Nv*t(Z} z=C({=F!iQd+;gTRUali&VHo6^94Mt(WNz2$(kP`RFqxepfBfl{jCgjxpFlXv)!1=P zu_B&b&{z4$(O}rx+-s7Swf@o6nVhjAlc~AO9tqRa^}%nwUPL)PJIeOPBVu~wu=*Xd z-F>NAt)w`Q1(x9!($uIno!!@(Cr}S#z?Et>6ZuQ+yAjK^ zdQG?2t@Gfya59ekXg(|OI{`pP&d)A$g5hW)0tyPCO zBCUodkI7CJIv&Do8@=nhzHsWA@+2N{nYcooXtP8pTQqfVJH-Br@8xK$VTu+W+mza5 zW9XlH^iIawDQW>@7NAdezTr9y=^@9gsPr@0dLMX!t?_X8C2MX28*ya$!t-8q(Q651 z5t=6b#DgS|0)3{RFFE!Wtp$yK?+=^X7G1sNPIgFpcF#`}yoraYt#Ts2{GL~INYFz4Pp zy3ZM}_q(bV{)3woecdk#zr{>7b}nDBeC2YU>vF$MxYj7!+|N}6 z)~pU4QtAcIy2CA`ZK2ZWhk^zH(=!|IIu^N~h6+1W_(W=UQi`^%9z)*3^cRyjpp9AO zR%mNkx2!=$kNX3vSq1)WX67}8bMvqK%+c7q#^c=N)}h_!wpLnnd_oZSYsJIek=G9X z;-*UYp0&{yT*G}BA{_&|+u*RbgYH5ecc#(HTl4+dk zCxtU_>o~2LTegZ(HXJ$>3|Z=-Lt6M{+~a&j#h78npO1$wMqQq3fSz(5Bg7m}op`rr z$((4_Q(AHIA1yw+pZN6WaB=YrJV;6ZC8za#3+Od;`Vj1Mo5ZuBQnUhEe+;CDZO-Xp z?vjzDsK2A+&8<}&wW6<-aRFZZ#}Bme$ilv zZPK1$8g>X{1mfEAe4XyR0BjGfct9At##X>VLG#DNk7-Z{)()xwd-#ilpgK0kwzwR1 zO)f1Xoy2*~oeO44hLDs=nCYvr>ACuo` zSX0QrF!Eb=)%x=lZMQI=xIaB1b<@;h+Gu5wi*KR%&UJWaXc`?@?Rxi7XHNo@5GV#; zg2k7F_LNzfGBQ#k-A6?7jJik0FMc5#1SUDeI4b}{D+)BM=_hrV$POwbvORbALlf%_ zjJx-GF)EnQR%@t*_&Da{!im2TMHmxGkDo1uPJQnuyP@et)b;Fe+i^Xa-+r55NG--Y zEV(pCQkNeAD zn``UEk*h1+``VF|vu3#!~k^H231cJ8I$4p_zJ*q1~^~ z*0rL#o9DbNyqRHFxY1*;ONt3vq8^y>V)y*1N2e4f60NHF1IQViZPFW?=D7PqqFckQ zqI_X4=s|9%w9D9gLQE<&q z$o_19ZHr%n#15jLe0`a#hD?40t>}qQX&`?8V&!E^xnZs*EBB|_`lqz8MN)(IqSekz zxp4kal%P`wrIOq@0+a;p+y#|2^ua*_Qp$goZ@s9%WGXo+0c~#Y1e3f*nXX}FwQX2& zU36-P7245&(jdUPT4^MyterA+i3BqOTgNKcZ|#Mul^yRNo@iGdeJ7kyKPHjS@(s3n z^~|v>eS4*1$`Z!`)0(kdf93P(K6@5%iLEc1vSB_jDZkW49&}iS%T&>pPRRpc;}!c) zZwYig30MCr6VuF3XWAK;dl+llroGLxdEu4anW%_ih{(Zc?B2`IkkwU~a9vyT^L&dpBwA>+xKU?7r z^c6d-VrQGVKp2b2Q-QDMR5eG&VvTC-ob(*oYXhySvegZu)}vsmZiQ>BeV@x{qhZ;! zwJJ`_op5-rd&;Q}je0v%-+w_k{2(S-j1t^x-*KH-_>vOMbs@wuJG;jo*{8$OcG<{2 zeSs=qpWvogmZttdfTMnjLSjoFo6^fB4@$kJ!N~E_dWnbbrKv8Gonn(lt$D=Dc~m!C zp>1ismz#zMLTF^Fkb`UdL9ULJ%?H&-4*#`rb6|lP8ILz!wA6F7RKwH@3spHcNyVh} z+t@fZ{z=@*6%+UtSF0Thot$LneB&qGy_-tGEggDYW(dV+{l#aCX)9At`JVrMHZn(+ zHga*vDX`D3%l~Pg;U?i8eo}U7w|&cxG9wJT>Xf5Q5$&8~5`j5RZ5lb_ zwF8}28cxv!P2Y$KcSGgI>>d~jkkn6Q>nS1S73T~akhW7UrRYy#T323tV^Xr;c<}4&sD{Z)C1{3dyiz5%XP)~x2urOH)zScCF0(7 z6Y0&pg6x{HjU$chj+6_z&z7RPpfaM@v_T7M6Sf)FIg1B0o*d3r&uq=%O;KXop)T)X zYv%WdGYVSb)#=gUjc4u@rVO9ClIiYr6=njUKk6jb!+% zR;aDjL&KpK{gewB)CV_5xC6d1%rg~>*?bRBO|SS2#N?|GXTLDK?JACMv{t9|{<>`0 z%H^j4a$gYjX?v^+nBIO|7F_xygorPpsc~c9^VPaP6x2XZ|IbRZ6P?}A@EwXYs`Gl9 zysdpo3zDa8Yqap8kcWFU-Dhv|k3w}#I16efECKF#aRhB=AinUh`IZ*5tc(W_EzAMv zjQO;=OrTwzE zm`4r_Mw&G|a3YRe_y4DtgTOOHOiIcx=YbnRGSpo?8()sIq2?2&#(Pip`xBG$ifwB| zDhk^kv~u{I`bxH5z%{nF&vWKC#2tDa+)JKO1v5{Bpco>8a%Q2R4Bqf( zm%my+yGAOO-B`#_k1g3Db;&>?n%?Mg7awo}jCxY@$bivn1tL_Mtqxv?DIP+LSBhHD zRceQp!I(2!$d0Jb_X{CO{Eva(GsLQw!o*cnzwvRZrpGyxcQEYgrpHkJp*ng}^h#V5 z%nwKlhVsW8Gg@s8F)h~>irOlm;$A{#4o4tcLT{OYZXkY-Nmeo&sWn^uFcy11Uhs5> zE&g)4`8HD><0JK=jZz``d)$?A6ZTrI_B9G0OdZQ2tu{51$L}BZ?%SN-6QtW(V{Xz| zH2qM7c{kW{qCdK9*mqM4t>7)%L8wLBd|TFflzHbHQW`GLUm4ChW2uyU!c9qaxVT$l z;1-1|G$SD_sA zn$tztGgq2>5;9+Nw47>42*B?#=7(w$0hXx78dgyp_)BDHxJh;dZO)pZ``9X_Px;*3 zcyTB$LdTo_{Pw;5ui{_)8nn7HX456vpRfl?eiGR%baK~0^T2vrCSD^u&V}nQ?xYr&TnIkRdc_{ z8h<26-t0AKXL0f%5{m6^lTM zo_lK@wEJh&^o?Sr2MC$8yKU3&nThh0?dHSxQ_ipXf0TW9Sd(Y>e=M)^)lO9%*vm@tC1RwV*uZ(_q#rc4K)&;2>)e9pMfJ-_Y7$%#?*iNKk;>%0{?BZ5CY;{}l>!5nB9 zT<3>TGGQ{30XQ z!Y!5yvy}J5tx(MtMowrSyworMy;ni>JZ&zvJiT@)sn!6_qvv$qsmSIwA$3VDJt0^^ zAU%Uw&23-s!1@Ku_IApe*0`8GILUO(3S2wE{DmF#8w&RZcV{b!5%}gV^PVaHlni6P zf6a8{wW{K4{J22bWHPesEdrYmDaJl$;HlJw>~uJ2Jp8sO55d^qQS+oSnqz-=PD|X@ zUc;>-o!eay6mt;DXub}hEFN~;FU5Q|2j(Ro2Pu<>@7J^Aht8ZBUxDvNJoyWrW((0- z8e1>5gL9c8kMaJVfs@v~Ay~V$xSXGfwEavYang~%+2j@qT3vj6K15V-ku}dL18y=-(%F(5zD$HFHNi!%U0&g2R*ci)PcVHJNta=B)`bhvfKY zWX6;Rr%U60p{CqfNu}kbmtTzL#OoVkbAAVPkQSGt@VGW3)q;Bq5#3-Wj(RkAY5a>} zEB|+FzOwDjH+Nd^fipLCW7L-n$(-{BLa}BQc$5w(;a;*(_3S@>A(?m^n8!hiaLI(J7M8QNSjtoXvl)XAu!WF;G`7$D_Irej)f|B zb+uos1C(1DPk9yx*)-o+X}3~W z%gK|BZjcnUpO0o|qK_Qu)O3AjgoXVeEkVlCntxO{r?-EB##<;a*gs#h%&#GtAz440 zqLiwWXfZFUn6`Up#w-P(s%GFijD?Yc%%tR-Z!&MbtSZ>K15@f8G$zQr2gBw467s97q*3rNge9F)QcHsS2u?nD9o!()aoa2RR>GUU)J!8)is#7_a1`7gCnGM&A};WPzH1EY9a@5>pV65hKo87>C(VT6{mYr2?J8Ba>0-oPVdi!jzA@&4rskZY=2G1GWSRRzDwK^4@>Q58u3F3Fj z@hsF<=|R8kt{9&vOtu)9=!V|~D&(KiQ7VvV?QG8_EH8|$q1iI zSyMsWz5=I)DF;pQz_0VlA59MTosVmbU3xVJWGTnk-&iwKx`X9KzA1G!Jm4)`WyDG@ zlyIW-gcdi(jXaClPdffPe$yP`7OKm$p!7MI2{m;t^{z|Mk;a=uUBbLK+8M-6@j&#t!lVT1!+n)tN228ov1k5}~diGW^ z!5PO<<|@}NR*PF>M8U8~iYY78ZeRe5AH~11VLH<<2Ogv}IR49y#>%E0a_5DxFY4VE zuuZye;+6GcIH;heZYGAo`-Ze^qqw9oDf}0lw`87umN#lP*~bsT zbUb0hiQ)vC^1l&X%@Ai@=&Y*GsoC#Q9!fH;b-DSAR3PVP{?0~4(S^u@U)cw(Vk1$u zC!6@T@E`T|o6ap0=M0wueX7!Wa?C_`5cQlTUs5UfT_R+F4ay>=~G*qCR-dPqvQ_b6!b%^t1u?p)ZG5rP!PS*9WxYofrwTPpLbYedYnj9s$;g)I;j1AR!ljm{jeIswi2Yls!_1qbLd(z$Q+3|QiF#taf8^7=G5AyIy2_WAYO44h-GK56vrGrrPbgavMn z3a~5ICCMa>|16%xz>R5BtZR67;KmdG_6mN5+4lv^FA034+B=H&>qqH+Y>PmJ0S}Rt z>UW8eG-o;9k7qJnS3+Vq6A!HQOWYCVT?a2-ZX(Vu`E|U`tUN{;vn)oACc3y&vK>Mj zl)D>yof2|c3q{x@RW!r7pBQ+*A@plaR54KG>7;|@VaRan6szjv$EUdIz7MeIl7wP4 z+wyKF5i5GHCo*g5h8wa$GqYZgOUUc4UKj1sdw<^QLyxVQxg%FXOfNr*5hE@^$rhM< z%@{+nBnx<{7q%Q&u)zm=KulLzT=Kb1aFZb2q91-r0|1{=DrA9!))nb~tXq}o+*ezG zX$V%`AH!)L+J|V?Y==T1!*c&WmKE z`#opi;@dT}Yy44^r;(`H83@&LF@)S0jt6!@<;7H4M|Bhp1LvPQOt~IzCbyHJP~{MgYaq?+m5GR%0+83mutvDg-dhrW2U%;FF~8G z(c^%FVw>C!1fezH7`JE>`>Qt7k@zS+Vx7Qdt@0rE>$ z*3qdv5em+ptiBzh*Pi~t5UXkU#+rD z&9RDhu`x@@r@lLaMU@1)Pn6i^KQbXWqAmP`g00aHWp5YxT{LU-^O6 z>a{v8ffXU%%Bx#11o>K5hPK?si7PEWf_|4VHf4gvaegjHr~6OGHDYzr(*1h0aq5RD z4O4blB=)(sZ31s$QhT=%rnF3gMYJ;d_n-PQ)uP6}#cJIM+(~R*R+0~0YbRm|iq5{v?&W!*8s(EfTKCEZN4;b- z=Lej8V&n{XBTaHUTSQ?pW$#_~a&mblo2*^CNshRM{YyMu=2jK?=GG{8=_`JbqWbFe_`|8kw3t~^Hb>9kb4?iW> z32X~HEGa>{JJt;-0FD@mLX3lQN{SKI?rgkEUfaCqmJonl*GFRY(lx2^B&Y* zz$#@foAxr?dxxhUn@|~gDP=ADI|g)yk00_&vlxcwexP0c(baLdc9NqIYtm>e`A7-bOAjQm(EH}R^Y{8xn&y)x-4-~ zf|LW0huax6tvs_spjKm%#aEb7zm22Y4`qV?Vu!*6R(2iMxc0h^Bsi@yQ+G>nm&Q-) z+G;X&p;LsW>zS9%C4Gm}8|(Oo7x~+QTxY86L{?YxZcPde+#HG(@ZS}Li+e^E5Y!eDJrgj)TG_40 z%H;l54SsRVREin%GoVDaGfg?27QCfq&STkaru9COXb)`P~Y_m=P06 zt3ntM07a!ec@jz6mCDWFB$0V?VgSbmk+$ub^X5{BLdono*o@?I#FH?uF{kok|ecwkO7d{aC_vuY@A8x+~HfsUMIV0VjA73UhzJ zdFXYP)1FR7-h<{e)=fxm@Ju_1-bhWXb=8q=NNMR@Hs~RFJH#m1y*gIsE6BSxQ)Rs~ zh~{C>x>dDnAn+T&HnjxWs+gLK7_vWYM=F1nLHLa{v{mlR9+;CXJiJIyV`$5Fqrk-Q z6ITymR;Y9_x-Kh9eVWJ}4Yvi-kC5(!YbAr&3CcSFT&EgQDwYt~?+pq(zn5ECV+2V} z@p9$D=6?h9sTG2AU-nu%4~y+-tc}uAB&KtTJ8;QiSaJ+AQ?BBaO;DPL&qTWf$>ltS zCpQ7gBrBo>A)K}w^7|~~EfSYK_G~vO)d(_Q$}m2Tmg{d+Zag?;rkHdIP^n94+EC`oe z<*=G($!F%e2*ph|vSB3MaS z>t%$PBUD)V0W%jxs2+n-A&Dv5v2K1HRo*w@BNnvne+!uEa7|ET3R1 zQ8vnCN0JNf*65So6jnps8=(O66QhAzm323Jcq3N0C@ppZ`P9*kkbaUO`ELE-K4dI* z<$zRCr;;lpl7T`Ra{)8$`R zCm`4|LZtQE&i`r%rV=<2qdA5Lzr5+KHWu%2W3H#X#<#6~D=DXEI~HKnZu#|oCO))k zJcZADW6PnfTVn{>31hY#-n6v{f@#a`vpZjVV-?<@Hc?P3zgQcmJ~tO`cQ7m=lOE6x zryS+Knd79J2B}POE$BNN&r@YhU8BJTm$$E-rv#nAsV0-}Mbg6D^}dzD$4(}>y32`k z)1MJIsZw)6@qh&{z$tklweeJD^4k1HfI=7Sv`Ba)P{OSat_r2ZEL?(bd)4&&cyt z%w4=dV3yYecON|CK12hg^rc@{izCU~f@ISpZRjcl%X~cW?YsEL(f>IdmC8v!k#Rz6 z_C8bA6JxZT1k$bm(Qx3m?0B#DPx^JN1XSQ@+O}muaFS1pEffx1ct!orfaOP6yTg%g z#0BLZ6_Fq*XazZ*aq1X+8(pw7{8WZ1jx7i{SfJ^J&ZM8Dw;S|jk4%|ft}#iP>Tc{) zLXY_#3UAc7KPIz|&iID1^0!LzZK`+(pCzg^WK)ZYT8~;(1KhVC0ZhX3)1uePSg$^f zn0exwfTiyyF2Sf@I&X@8S#^4^__=c4E2;(UsMbO_APO6)c0C|RaA#%V%`%ox-t_?_ zwI@gqCkQ)`@gB4%Tp2_Zg1N@2Bcs3-TYR?}HhfzU1_)GqG4GbQ3Molw;08n(b{{@< zjeE;mpR}@bq=>e*&G1Lot+V=H)X7Av|1x^y>zO||znswcanNqH-Y&gSn>WjB^9=IA zQj%!VpsWl?Sw)Z_$?1F64)IEjz?yxWIRIauHd~NE9XEc1!1j4+Pwrs$lNVPlx&CWF zVaiMFQ1WLQTvCe5dYSNa{&G|I3(|P3PtBb88?~fq@6NdAOAkO2=;fvTk>35&{(!;f zgkx+fTN@R{!ZBaB0UIma_9DH}<#Oiifz}o`BFpE5p<;fv0`q2CU|T}>WI3ezM3dge zn0nf71cQ>Q-qEDSF4p7z+Ig^{#8-A<=6fu+_ z$%2~{4M4`&1yKK8Zjc=~A@y_L6SY=o69_D>mTk+)M0QrSD`*q)=Q-@g%66Zc-w-Q7D|1K% z+&sTVG~cIZn)ew0tN$y_M*eUHB+1qPNstW6h`;U2XbI{~C?PE$XkoFv=jq;ato~M< zELLMV*BPbbvo|ycJ*uHYMg0O&Pn%}+yc$iiIABfKw^Az$Z~{vcO3hPuY0#a)gXLC! zEFcu{mkXeL9Y|GlyvJn;z~z1*Jy81=Z>g|nPectwx?*{!P;ar&g zg!htUi|(LtxrnyfOqniEK7M0mhYYRltkl%ry^_c(F5r8uT< zf&ZY=XmvO+pyH3GAC_NwRR!fyMYod3@62B%(QG+I^fNp*6CSp5Safjq54rp{I|N;q zNJOz~RgBjCdV=1QEJXfw*@5e_$9zS_A1Qb&fh)?z?BE?wMs*zE&-4t9eVtTU#`wj= zmzhuKRwbzh)O>~Mn<^{wRP(IK8)*!mUf;r}PJv#1POoK^(}q@1(mZRo4On&#!cx_;<*&%(Fm6cjNGw# z&7MquxCs#7%_@gyF|df>&fCHRmHRl* z^sa9%0KPf@^`1R_=Q{KKC?E$xIJnriPL6i1;sk0H3(b(8Gh7kC?r#n`36vjxI#o9% zIDKl-8u4yQvyePQ_dB@ST2GRpqYGZ@YQbI?_SP(vO8L*+7T>v3WBhv|Lukml+T6>d z6an$Yy6gNEx^{|hj};S?VnzY}gixvN;>cI(etWlL`wBGIRyjVVYcir!zR%&$my%Ti z@p2vBY}C04GyAI(Y8D-f0S{~$kMiX!esd);5#IMyLMP|U#NF1mYLM0e&(0W2+QUlHB}qOv)+i?l~;VC9l^8` z{4*#g=?bEt7ufsv?ztoXF6^ZTghzVnuJA=%MNV!YJMgea=~sf?gbC1#zwY=qof8QLjJtFtxZ9 zrj=Y0i!|1%Kt2x!d^WU?R^uvV0+rz$TG9?|+=GhSv^w=nyUvxEEF5BLA(}ltz99Dl zU@pZw3_2FesE059Z=#ZfY+C7v?oIMdpdhJ}MPZl-#zPZDk7!Ad!PN~g{;4AdOZ`&l6ThPFMq- z{E;ujw>_f`t|);8x0PQcq{*mBXvX7gS;TUNSk+x{FaZ+T)eVsW#*NS=-6~a|*cHUZ z18*U=?kxrdwQ>G4Z07dC@5xTp92^xH<70Gd|)Lxl!k{d^qlWF^^B|mV0(Mi@C z)ljP(+@U27Mc3Qa&>_+t-uNHRg`9S+O>>Y1-ZVR&asn+D<=(YWsilkwKw0#TW=g1q zxeQ2BlJ9D8(PP@!)a!#N5`C#Q%`0sLRCr5R9YHDzuHwq4d!a;TbNbW+^%~`HH(^l+ zsv~~ zI11}E)n?*_&f7~21aP}0kc}$^HZC)wWD+>ttKY~>>{^`z*~}R(l-J%3@l6MDt6ns#fkwn$Q7{(R4YYLDU9a0Bn7+mZ+6b=Q zU_w_>v|t$dgPH~PKz1Pa(L>RS3mSV3AXY}U=uApS)LfTMIbZzSzA1m6t$Dj^W}A)i zW&ODGiNh`?hI2Jx)>?eoaMx+i-o~FU6IJmIp$aFX!XCArRX~H>q=zxK`s%!wUkZPG zmcTh2x=>|83&(0=tmk>L=lh+I&YiPi{k<-!jZ=08)V0HXQ|1x z)5iJDD8RY->c6%4FM0SISF{SkL}FX?`)pZQ3rhoNOqj_67#!y7 zSJ7hInZgQST%T(?kp&I7PT{6LPKG>eW^%dJR?<5mLLk8G34)RS-P&=ZHv#QQ*#W2X zWiIHn_{u)$x)b9y8heh0N>N_XLSV#0)Rz3_y~_JQlr~(ZAQd&H+K);ZTWFj*%d;*C zwZbn*9a1sr#T0t<-u)5h;Moj}{S>%f~%(Kmmn6V$ox>7@G4dzkroELLaTwvE+nK7WtaU}XPR0>#psxo=ULrrHt2Y2pzI??Y|LqM7%bUe za38rc=saCH?U|z70*c|M1?QibBqn8!tE!&tm@~HRQ9^W7CTO#E^a4YOLDp9T$?Hu1>=9%}L^AP`9wZ)Z!{kQq; zz{AFe0E6fOdXZi>a88?(=J2m8I)aKkvMoLIwa&Y zI@awm{-kXjc{%@RKVNb_psU3gV6SY&;z2*z#r@bx`%(ic<5_N3xEqhzwF7okvORTiI|sC>{q@)a4RjE+WahoA2ycPYT@(F=_Gklt$3ioz^8*z5fz8;Nigf7`VG31 zbSs|b8e7R--u=N1gZLsmVB~1)OM)iD_)WDbRc||1Vp_7{vp=BGteN+<`fB%A3WnY`eww;5Z zd(YM^0cX>tzEK$}>E#UCLJXpQ` zoH^Fy9J0p2QdpK!mTfLKiTJ6tYIPtr+C?>9iIsIq9lxO3Tn+U~ZzM;Rpv7FgVAJJB zy9j#ldUp7Oq$Q#A;)?>Ah^j)vl&4)L`P6OVvlkpj$^0-?gGy9HBUMO!Q_ znIiKmR3tRZ49x>V8cKiHsh7b7J#&|xsCPtK$sH2>=vh`>3rxYiOeWGoYHSH6wo%(_ zRP1AXZ?E^*DO9&ckQra&W9BGi|v33 zwVnoP99s5sRB=hK%(T+lPFU-6FfWx*q`mq=`#-;W+9(l(KYl{YSrGEKZ!9!iW{`pSUUZ zoFlW$khET(8+}P1QXzeyLJE<`p_Wfl_ESNa0 za?3fs9^*mXbupo!P`v(L!uze*;HYxT^W5&)pzV}m8A6)7GGLkF{?kwV`)kOh&uh29 z&Yy+eQV)8|sJBI^OYjA~NB96(;kO{hd9lmm+U(B+V(ClN-;Msq67I;pDAi>Qq*CGNoZYQ*)q`)UaOhz$tq1k>(X?*<5x zY+ku}TY!W^Kj^L~Japv95qA8(Xi1p8FmU8W-bX}j!J}!5l{-4i^lNL&EovrOz|*e^ z17y88lQx~%dmb^UWE?WLj{=(Ai!btOZ8=-FgKGPSoH2>N#`4rP@Vsl&i3j~MGpj&cqZEJ!sZy89G!YuyXlbH`l8#0X_Xj4Mymkj%s`M2Q=bk-Qql;Iz9+xhN z8b89ic>SF#BLf|dwe#c#v|pbNjsxi~){l`$zdt4uZHCN}uRzN#OBUE7Q@)u26AEm$ z%Y<@IS3fMG2=rXo9KYYN5Yby2G4h2`3GEyr>$-8=JbYeWsTUb?Chc_ocNAy7%7vGp zc~KJ0xYjw))#Q-1Wb-bKMqjo}QyqBe(Ani&L!5t5e2_ZM)|m8{X*=&As)|m%J%6~T zr6gt8fEuz;V6@!&W$x*XwOPwmZsaxMe+8+b>SGpwA?WB_QmwJP)zmzBtn#J*{z1kP zUj^+y;_qKIXr}#jK3v%hp;204go>`VK_GP}@3^jGc=vw_2Q~>ZXqW@(3+*!RA$%$v z#2dy4rY?K$Qq-`WIAjtMoFW_!=@z<>j4PgYmi=e1gd8>Vo>^C8S8{>D{p~i^$m+hA z0lN3gI!YoO-uVPjzMAq`TMSjpAB)#gK5d8Tb4e++@;n;e_mV9mM_sTi8YgA8%vmX( z=x{5$l;y5+nV$ZD${0UTb^Nm2S8P>7ZJ?Q@{rfnHP}SfXXzfFT@aBy~1pwudBSp=# z_1n*1LY?T)rR9wGiu0e9OpW8lr{LmTFmMOAvQ(=6CbUunwPbCkh>*tB4r*6xgMw3* zPn^~zeT3HN|IC#im&D=hLNFm0+<#rw$QiB>qzoVauYJX4dd~mllL8}FAj=;_U(%>U zQ1rD&PE!>)BVbk248ry5usXH)Yk6Y@+TehDwG?DG;U&k_}LQryqofc z69vvkeNJ7ibzTeF{;riFxhBbc))NwPGwm=bu>6`R5CdR@e=CNuCvwZ&lBqqc{z3wF zmYzijBvX64>MK|2qn@Q>Ph}1%lot7>p-ZYuL7wwkp;sJi^iLoy+ zu&J@v=D4Typ*XW}G$M-MvoAx;tVC6K=#Z8^6LeCPne6#YVX;Ftl@Q$((goG4tN(Hna}8 zEm=8yMbR!!6&%Fgrx_b)XVLb68twub?YJb9q_wU6vm5H61P-+Z=luYzTii&;tq=!ymtC5vjnH|Fvr_`jmMX#1ttVqYQkMizPntr?Kow zsWDx%U&crA9nmWoZBQJR8;nk<_1K_sRr7fedpmWzT;2GGibyLIC|S3vr8n}JJ}G9Q6LnOxI;$hAgX z43oQLUo@w7A2Ej#=Fcs7W(J0>-0WW%QfUi4nz|HGx)!nkhdq@0^!2ys*PdQmqw_Tt zycY>6qF{?gy$#I64#kAVA}eDJmxW#R0E3byo_>}7MTd;e%AQc^$}rE6v4DZ$nh3O# zv74rC7kRBL99>d?wiD?nf|^xpoJgUV0Nb(@K9MC4b}#5F5<_qJs&D2}{PV{fp5>I? z1ZYp&276fW5AM@ShGP~`!9nwgzV}^03&!^;PM7l)l31q{%F5A*GBRGmbh_h0L9ke^;U1WIUY8lYg_ zdoD!-4!T*1OZbNFiIP6MhoT#lW}{W>@4=+aA5zIg3oW)(6jPC&|K)y;q8BBAc9|b@ z%`}$EH0ixy0GdFpkrmDZTDrq37xYffsS$ns3_#a|mhR(Ku3D5>v6a&yA5B9|ZQGVh zfdml%ei6{B(`x~JufrzHpEjSelBq?W&o${~7z>k>)4V`EZvpPoxbBTNyPf_{owvX& zWjx_ z%z1$Q&FKlR2Ou$ZzX7cSUdV3!n`Dw#xV~b|q>lu4wO>OJ+`IS8duH7SY@WOO8z6YX zV8VExZ-PfJt*4)U6xNH0@Fvb0`vSEBD&8N`tT_yFg0&HEEzR4RNiJ)pUep#`Qi(QN ztiVcwWnDsYJ>WawXwn~Ynz;nzQ@#hhN+5Wz($f6}Mqma<)BjN%RtGP3OP+GKPvCN5LB} zhd^J$28Aazg0J;Da+c#cuN{2>MOR%soFNV(zPzaw8);F$uCh`>2RQesYzAUJRHXDe zW6U{hms|JKsPEUdV1j?$6|WDxSHAT^w1Gqfg!NB$iDli1?<^bII|Vvq9+{Z$JQRay zv~;F{h}y`>2(yd%W8wn@9O<972rOKBUA-%jQK6}F9 zZQ7u;)4zC|uB>>oEVoC;P7b@L$3yl-hsrX$F=5>-1$8#=_sz6=^>`kGa*uJ4RXTxtbBQ4|sQpmR)VKe{-*js3exuWB1H z5b9sm84jM_ARy;0%>NKMaaj;nm-`P<;h(fj(zjpukGZGFj98Zd*~?|7@+S>ZTs=co zbZltIIW!|*>ar=Vz{OqOW7>!47*hRCvGYclKH9TWUT@B3-pV7=1(61f-$4~WWdC75 z&EAgqBh8BCdn5{K+kB2=HN5*}l5|^4)yj}mnFMYms>FuCHM6(aFbJ8>Am41N?@5=H z8P**D*Ok3?|97}+&K~L1M_@*-oHvs)3^k-)IVT3ss0i@!Z*xr2%R6yGF0QxZuw9Sp z$+oHRy?JT0OV&s|QTEe7x%;th%`2*T5B%LjkmR`46)$!QR{>kS+|CAKIBW**a8ULT zu^r24MV7Qa#Uui;J0O}SAH}usH9ZorgocBqsmrhR&;iYX0lOw)5!wrwh!B*yk zs*R1`TzLoo0O7b6KcrNF0k{qJ_4a?cDn&}^YKKI}oUXY$GYYXH9&<%+f9&yt7Ym!3 zR?KypsJVO5X~KD)on?9w;o)T+imoYbXwySfztp2011fl9%2%3ts>5Lporg;xrw&y7 z&?LEO@_vuD2^}4W?rtf^m~{%sTq}PWbjtphx*(4c&9{;h@eDkT)qGW>_*^LktXQ zK$}M!kSju4EvSsQP?+CXOox{jLA zp}jK0f91CxM71a*nYqTx`;dwK!bYGxvUH|$-$n1a#j-n@nd{+=>(wfj?~mCPHatJJ zXX&z6s-o7{Q_j@W7YU8o=~K#;+1w0rK*=6WyZ*zbYd4Jz#pv^xGm~fzrj@;z-?+!M zY7lEZ?~Pu_Kpp4%DS9MVr#MySr%lc3wR|DJ!P<&@WhNk-Vf_z*z@KbJf3rk{&GUm7 z2bv^)li_P#t_iPhE223y-{{}>(^crio`i@M%jrNVOJtBy?VeIInG?lT0j5Q@ecZCe zL6ICzaXO@8K}k$&8_D*tI%R0cRw1~+nDYPU zMq9kU&oWWW56y<{3l#{~-EdMcn7!}6vMmB#O&KH?8xg=nV3v9tPlv(Q)8hYs^6_1Y zYb-~(2my=Vv=)_ApYXvKV6tm#)!&CEBCz24w;$-dL$F#PD<}@6b5n6Ws&6b|mOpL0 zL8Ur&#lv+)t9Ltp8|)PMU&=wMz@!(nkPZWu@kJeRBUXB4j9@^d$`@+wJMynQ)wX*e znHq)1LPy?p5e!|4+QR_Y_Et;lJk7-u7)1J`hgsQzFqy=Ew@P>Oj{IYMlIw-Jco%h8 z;m!@MF7e|m41$5Q&_&+=R$lG{qO+Awor9pUuw(OmPQYh~SJvCa;+_AM5rY|5*!QYn z`fL_Jg8{7NkBv0lPf)?Z@}-Cb-AnGW5UlQ!^xsGQpL_Lf_eP%E#7IMavez4YS^$pm|Hl`f3YDH5t<`M-$7rMe zk6#_s(IusovxLL8!s1o_-A=;3Q`u0MW!KpG^;9!(64n0>AK`bVH}LO3B<;rajLM%rkLg0wT# zYu>pKn7^MV3bVKRcSiBU!!Ow)#zuce{SGO6@!#y{e_+9*d)D|`Fk!p(q#d{^ViSV7 zMU@YG(}8Nsr2XCZ?E7@!wmL+|EyA`O{tC=8pu-bUA1y|}#;6^p+D9~bPqn|~Z>w9q&#P5QlVU!3+%TwHU;qLI z%j18>5-dmhy^Bh)Vk;nV`lD zQpH%{uIznv?^K@y4NhRxzXj63^!qIvpKsn;ogLr1PgZdK4WY!j&QF@)tW04g@qege zm%;3MFvu?Zp~cfAF<1+jqJB|1KYP6e?ev9Ca3%@l0_WEcy}uVTEEv%c!-?JpBx#7v zt<>bc)&#gxf!;^rpWerMcsuq^6?03W3)0I-hOb%$D0V8xvo=~=n3 zy3-qN1iyc>8SDS<^t;jb1=mzGVZte39?dvZwuMp-t0J(vlV*SUu~%8?S%t7VqFJXy z--tKOOCN0=1uBdOV8ItN{`||Cd8V7c3?})sq+7jm>xXy%xGy0`&%ktj2YR5B%Fy3= zs-7X%5FFzMjk&S9h_||1Nk`y|#8E&x?nkCVn|m)8x;+>Ai_Z+*Tr(1&Q)bi6S^EA( zHi_V{ftd}=9{SkMFj|eZWix){NrxKv(OZA{(OB2bAKe2!Jw7)$3#N3Od5>YotWOpf z-0k~uoKl%JC zMf}gGHy&7+44Cm0TfZ43JVt)9nIEkMEI89DmJ@vdW_ipID-#95SY~{bt`BZ?z65Sg z|0wxO7UnPUIxSy-7ur`WGroU1Q?QQs1DMPNS=0HSQ8h21ro_Sm8w4NM#gf`^>rcbp- z*;E4aISkF!1_LNQ8fa)yu>QoX$W(6u7#P15j(GxgHA_@`K|A)D%+^1OZUrMaAoDR6 z3dVJ%x1?~^Klhilqgx#eOrL?Z0}xlcnsq@`{ta*g;6(GE9)3KbaRa`sdnGi-IyT@7 z1CvKS;tG0lu)!F($an;>KrkK*6?$^wZ{=P$Q8~M%v1~(;a>Fo)4d8;FDu>k3|DlQ> z#^L=t&VUtv4u;C6H(BNHzoZNBpauvd@N548DGy_ytM=QYIe+UyEO+65*l<#qjnkKB zKecopJ#27y;W;qgQ4ltED^e`Ub(71~jeTmqKS8?vlI(>IF_(ckI7yBm1~P0EEtQ3I zZw4C9lKKy-+Tyv#2)kVf=B^~6seD#ye1AnYBOd@M$@CxIYqad%21;HV0+d{~eYMUv zUfcYGZpoDEBA8N|u)-LiEg3e?@y!_Hia(%%9Ce&eckC0dTv#n!u>oz+Y!zGL&A{_3w;7y~gNSa2Ef%KsprE(}0f z{HpX}9Mq2XxsV_X#b;#4e+A6)x@naGF$;)IbfN9_`P6(vpA5DN3HcJDSC)!EEQVBK zDhS2v#y&&M6$omXNr!`$|5x<}CA2eq7x?@3tcb^M2RN|)44-)P9`G?Rr4{_u6@iZ_j1p18Yr9~S!CxUblQjM!Wd@XHqCQ&@{m7uPDG(<33wF#**SAegl0IEhY@-I<7#jHw{(>() zOm9463-}xn0Ackrj_94x2*d+2NU*xA_>Zu85ZQ7A`K4#1%{zP#$ny4lCen@XKqCG8 zJCu2o^*3W1f4ZM7&Z*Oz8>LhK3&eIIZhqX=x(ljc$KyT|nHKxHiYDYCVc(oQ4h0wX z9g@rrkUU(+@RiR@j(6hS%~yw6nV-K0{Cyn}*_oDy)P;l|T>mM33?5Y!@}odU_rXP) z9~DU-?z_<_FbovT^s_fXSz|Ug@8P|Va=CsnWPOSF&mi04CjB0=`#w=2EpMdUGFZ!> z$&J+2pHZY=joF9SJKizOy zh5v1Kwgpy}qr;~sbwqQ7TcC`f5yp5u)&psSKj(sAul8*?vS3}L)?^4G7-T#uXKlSf zGbi5J94W^~0dxg>{W%K-a|r<^352(GL`j0LF1!vzPJ@ex3ULWieH8vC@_V^MAhl->O_InPW1u|Dqk2VZsRmL0EGs)=C0Kw`Dugri)w=e#)h&dK0 zZIE6{9p}@x6fQ!oyIa6UY~~ZG0TmA{;G7T}8a@^XWBowtr%B)Og6bd-ug0?=>-*J* zPL+*KT5l&wf@+mQc#M)Ra2rn0V2MwgyB^lgE#F7*K;){W`o0eQ>1&7CRO7c}(x5o$ z)Q&%I)>=%j>GfFvWYZaw|#>7%1tZ7`R@CHjIJg6;mVUtTxFNyje3VKC0c zKeZIRvcs(Dz;S>P5!^Qp>YNcV+jj{u|EmI91#eC6M4+=we~^*?^Q6mW!$k;IyM_e0CSdhFgOps?I!@NdY-2j$RoQpnx! z9rjA{qA~)kPCe}QABy}#`j~8e;3B}gq{jaX@A7A1X?1{6>Gb^gAWD;_9`-i(suYZXX~HCFt;n?C<>UGU=n)nr^S#+6OHjs@+2ynh3* zKG-JBq{YZj(COPgbTIh09Kdmo|8LUDu>rM!)0=MV<{`rl-g?NLS&Y8peYh#+yQ z`+MT{(6U6f&Y(Js5;yPD5f|NMz)-(DcLnyFeaFBD5Sn<58F>6@-55Yz9}UK@UAAn{ z^=~P`F4MNXx%BVA=;rS5rFq@2=EeirNdBW?HWoak{sw@leYlSarhHmhLYh{PMVmQ=n&AcEa@i zBkC9p6^uz46Ov`icIEu1m-4l+A$tUrGf0)>Tc4^b@LAm&$I5Y7V6O9wjyU^I>MeDh;49>lIEKmD5@Fk%3)hD^vD1B`JSPd|d*wyCDK zdWB#iW;)hpfaN48Jg_JRKK$TSK=hRf58oz}Uk_Z)kJu7j;JZniek*n=1>a>E-JjglD2~hV#Sh=pVA4eu9wXkC4LM#6>OFeG{a;G~ zsGHCkvZWhPRxhQLX=Xf2S%EG7;f2ai4{P8h-ghcdW3K~2mq1grzklyjyU7W-3hBi^&@_9vf;DPD7y13KiM0HyX`~8|k=k8(l{{jxTOT*;Asvt#K$F=a(P#a2fGVh^`a|^oz7xJfVq0 zF$okaA`gI-ogyuASVzMBxLM=G?u7QB7-qt$<3v4Yy6a%LRX$aXQ~~h7xCifWo|8LoQlUbMa?^_ zerWUxq}wD-e&zmC1$QF;UQ%acp8WJtVKrlSK9nttxA$-6*(Yz!@kc?E3fO@Lk|;|; zT}}RA^^Fte?BTKo>EPH&fP&3|zS-R)l7L`YcaMYyd&YrvL0b|7Ac1&dJCOF3@|&k%*5*lhl4grj63pnr+s1E7@U4 zUwltQKszN`5ErWJOrE@pSG$>eILh86un5@H-KwPrj5R=b%M%S}uQ&A`Hd+bA|Ks>q z^`yc~3*c6{Qyr~LcYeq-dKL$3W%WJG>%mUW!EGEt*lgOyid^-k`V{T*5RspU&UV}C zh=BSLY@_4octijv|6w_;mTo`CYq9MmItxkN?Ze*W!t&4x=rXhh25)i(tW^=l*P5Y3 z#7!cQK#xxYt9dWu??fYmPZH6Id=zq($T|Eede9FvT;&g;w0JBOgl#{jii#8SMKwAr`u9&Y<70>JbzzRRC0 zj}JPgS*u_tND2hW!rxTl?`#)W=6EO^m5p;h`vmsy#ES!>xhMYo-U>TW=bRf2Q$~Qy zSVVGn3#!%f%5+cId5)^1t*F^6G%wd`CE99o&aiUM^r@xO(4hxAz(Rzu{Py~t(45o9 zcbHOp#o5J|tmw}4uLw>$K^(2+ zA5x2NX*LZ^YO&Pt**X@aEUxZ+w$;Al_O-04Uzr;UBEeL`0nx;<@{HBrj}BPeJ(9<2 zK5lU~uy-)7oMxo$b2y5G)T8YT90vsW6h=o}c?fzSndlJYg!i-wEL8;N)G<<>xwm9d z34RQr6T)+Jqv&tF*i>I1m=>`A=O_m zLbV>X1`b%C323F5d=2ZX!?{}9zIu!oUn`?jSHh+QEOUDyMtB=5(Pi`kX! zQD5%Rz~Eg_-yN|!>#<`a>N~I91~^A;I}eOY&U0_5bup3`N~}nb8wB+3ebxp}__OZc3l@5s zBzuT`RwjhYoa=S84NzLlPXU{*lOzvg?M0b(sJYU`p*fc4!U2vlQjsl)SU>jI<+#yZ zR@JSe;YYu$-n20UAblr}{-d)T3dT1LG&Ba4J*~_Wn>0tnIB=oL_RR|W>rZ2k+uI7(dikG81G!mKMVlD!7q$XNQ z4OD8`@1B@gJHwwcMeM9?ZhXTkq*9es01YzDikS&aLrO z25{uRo!*B#-zi|3mdUZNwm>h}>|z~8edmE`xSSuksfM`WLSqCsF!QJ{p6BqSq(M|P zmQ3s3Z|Pv5jUJov9chW%+|r#@!y221cv!B3j#EX8SzmPvTE#GAJ@iRblcZ#T*brVv zSQD)wZm^JSCz}B#yvq)ncG1^=t8@YmqI?p2Xx;g4#p9#?-dcR(bvoyI*3+M4AZD+K z^My=b2oEljL&W|A=ZJRvodp=}u$#mY15m)-Z4l41yphEmrl) zo$Ik8DTG7oJ`X)uOT&NpeiRYh1v3ep$+r)vPX0i3WRojm{%dq7$*hWKPw?%G;&iqF ziWsM3e@oZubI$E3OTZ=mMyKAGR!>TPsUn2tNg5O+hAJM2+wDqNSDO%9 zI#-+cf~ELOaJJ6rNp6YDlRK2{Rprn)Y`vK%)71@`aTF}37~TfX>YIzL za~O#kal;UCQn?6bl+fE;EzLv=sr)-?>nzaP>B|Ris{G2i%m0N%3bR(ihol-!k0#wDhA~{o?^iU(?lKk`!@f9DoWM#?VPaY0vW1uD8^(Xg zcd06|4V2*bh?k#0F^?bD&Hyr4P_~d?^j<~KPR|p-%)XJ;?4Z0raR-3wb8O-MVA2&6 z35gjcl(l$;508G?%{{0`waVFFCOA|1yD0hrO8HsLt4XG>WsF5vd8L+aa3yH<%pyo8 zKpq1${M`SpNcVh)e6UKsBf#>i2( z>$M@E6PLeXS9g?t+07mh`7zN4zYQ&Rx*d@|yHN4K7efgx2Gi}ELIdDYAX2RVkrh^J zF>TZ9#23WOwPv9uUc?4$PbGXXgB@nsz(_Few5JsQ+`UIqR`KTIP%DXU>gHmenH{^~ zi5;i`+J98R7*_$24F4;Ue0+W!!y+@@{b>7os;5JxD>+81~X#01ulBJUJ2hwlA&|IBuo@*?4L(RDSfzd_> zh622+6^CL~(dIkO{_1|)_ZNae0!q`w@64~83w0BM79^gF$BC>s=U);CU?$F~_&cK!FrcLws;vMX z`uK6bNxx1*E5Enb@;bwMlW7o7f*+VD)`Gf)-vpar?3l9Mzv_ux7zLWs zPF@F-U9HtKB|X=kYU%J|tc-e3o{H)k6#!pQ%EliC^|9gJ-P!MBtot?}MIc?HTE_Qx z{zavJ@N(Si1Qs$#hQVY+D^^>oOL@{RW02*fgxV@C91rWOV*HGSp@LL{s&!wa>8Y0_ z^;w3a+Shvo-J8Hl3rv>LMW>LhfT$rb zKaREm5|%f9$~XwEg9JmZQJ)P4NaZ%JXp&$Lv|o3OQ#d!R`c;%<#A*W|=Ve`GA0-4* z`IFTqxqs0OOZM85HtEm$5;wopbGhZyK$yE&82{ED5;F_*_qUO{BAfw$RS)EbG zTF;m9njgGcXnbnRCcmByyga%bn3~qotzb_Zh^P;%sh}d60^*ri{S?2w;r@n3MQ`{{ z;H-I1oy*0^rbnErYF(6HL9MmfPY`@FBB9RNq@ffUM;*<>(RN>|U zl^0S^0Ytn0JXCH&_|A{;6VukJk>`~Y>A_7}h7Jh=Ic&V5y&7&J)P;9_!5qz5nh#PA zg1w}0%(Mo0jQ-g6wgY>W`N3XkJ$F~6{6HKE%>JjE&_7Gzz4`g-70_>+==fKo@5%kl zm-j~jZ{s}Evr5@MnIZ%t-FwdW6pastUdDtI*6DU+ak>6g4U$G+VRrJsOp0Gay@+v`}1KDr3I|L&9&a z8^1|3z#H2}lj^PtP$UK_JU7B@cl~G5zQLs+>do*n7T#6xiG|JIs z9kQc9s23h!8;^OykuZi&B=9*?Ay6bFJg77|UGBpqgg5}TfnNlb6t(KJvd;zexPTpz z)zF(ev<^*%y6xC_{9wVc@&}{W{XEg}^qNrRR0?X=Yg=b)NR0r3*it@r){7Wz?TQaD z^G(Gw3c3hSIUxA5X}LS67%?k5pu6Y>c#5cT*e?iJ*Yw4Mn%hB^~y2_}s|M=j|i z0~0+449vuP1C#P)fZ|ImZsW_0VB`oDE7G68cy20;zLFel^Qs*qmnih+GZHh&lFiwx zmPOEi7eCpPCz)rI(lS7PqwGv%+?`7vEFzs~6px#ZMdGE<}~i&dfD=F1&qcQ=yyS!4bRYAGWrU-#Rz4pj{B~ z-Sha~%AHglAKSCXDxa74RTVbY$AzN2l)0#^r{NWakxdeKFkn)qT5-1QFH+lvrcGM1 z9Xk)U_^T=1i=n*>@jFcQ#q@&$N|u+>KUZCK^w0+VKJv_j$;^$Q-Jt0dLnIAKnz=hQbruCPdK4->(wF%Gw0Ga@-<_RZ zr74WoP}`|WR8LP+u@jbVv z1sBWX-7xLJ6|R=sp$dr6(C@dG-^h?T?8)3W8l6Z=;>1`-NzZQ>ZR~A}jbf%Hx&--@ zsY=6U9S(47pl{|!#^?LTcgK)$LkG)PXq8{*bGn>ob|0=G?gq(D6n2(l8GXXVoDqyB zO>7L5TjZS3=rz#pKk*WTH-YpZ^yquNmYSemmM;&86X#L$W6nzquF`urUrejJ&XpCX z33_p>Q7e~4{LAiX1n-qxsE2L0)=e8|ff?eVj#B0iOR7EKSx{p^v*zj|hXr112|Hg> z1DnJyh1~C|0;T&1jGbjm+kSF7R57ECs_qDu@$jlQJ}af*yN{YhR!7xVnpItQ!>6(i z=G<}nY^Tu%r%__@@#A+{!ve&RS&kE5d@!Lj-3OOm(!gzCm0aq+->d2tk{A`^B!)xM z(A5YPNWC-#zC=vkxYcB+^=R`3(>lOJ7!G*fpI>f@9D(2>jolCc7zcdcF8zzqsqt#M(sgusE>rtK-m5(GiE~*lrooY+DelmOsX{yam)^BRh?f^ScR?@B{JSd94 zS?w0tP!!f%S!Y#MNDxVmo$MTy#`i89tZnH{nvdg5r<1$0rjM104>C{=3BAeOB*NX_ z@Hg4d50)AO76f%Pxkm}6YTm4+db1FT(Otu9OX;n-mwVmx@N)MB!9S@SYS)ARZ(S&Rrja7m0}uYH!o4W>Z+)y89gHV zL~p;89kQeC zXSlb4R&IIRVP{?G7x&x3WHz1m00n$sb0sP>K5z9ec>_Zx!X&Wi+fuRsLY-)^O5{(Z zicJe=R(kJy47n|}{a4ne4OI!fV#(i=mAPeyO(w^vYL#_buHI6$FKcd{lRB~Il2;ptl@Uf1STzC}me!?i&cmjBXY_`a#{_D;PcRqmg~j0M3@oID%lpU<6XaZBDi znw$7isD-6brCg!PCjiqk?-B6A*g;OtADQh}yRKWL)7hB_yL#N6k?B1lFsrk}kf1mx z+qvKJGzUu_a4gwtU)Q<=Fn67_>PcM@O^!^YSA8pF6ch%s)HmhM0#b(teXY z9s_;pCmM3pe-F%t7Xx0OyM`LTjRby<3^+{lI3xRF(^ey%xJ#0MS`a%%1@|e*6^z5y z{1h-_c~JS%rtBmZ-?(@3&GMs`YX|RSv%AhF{0@(jUL16~uNrc$CQ;|;!W$Om{HlpU zp6a?=a&PjmM!q1UEBJ2u3(03s^rdvqJ08ATh8zOhCEO zx?6X?0j?nhuwq*}I1goz1pVbI?d^UJ-%qbaf<2heLSpU{4x5eZss`0fa{jzR={Pz5 zY8Jr2UVk*JZIK0%$`kLCN`*IRx+5sJLG{@Dmrw#HRy~hqy35OO7lYzhr1A0*QHLtK z3#>QMdO6uuzJVQT2Z~2sWwuQwTbvlRC6UohK`pO0G##ZSK$|~$*f}_@dVdhS-|vE{ z5h*F|+{=e7a~FfuDW$N2?=CF44IC7$eUY!W&_#@AUkt`mB1rY*#m2<9V4G7%Xni{&11zv;#%T0ufwg#2P zq+vTACqb)Hu|(UZo!1UG{Men9uMUe>Dwnj&R%x z*8h!d4Rl3lmSAYGo`0q{Tu6JcyenK2qc!k|w-w`U<`WyO+qgG{E&ALsjdin5hxLKI zB-~#CHUfZQy~7JHSGWv4O8^5h-G6>ZfFDf`0>+|Lcv}m(OE-0VOn4g=ShCi3G&gXJ z&n}M}`ZaD<`&4e?pf9T02RZF53|XIn5Tu5`mEO%Sow*>XUJ-T-(DCoYmbUJ|eJYH0kR5D)>o2 zyZ_>s*!E%N${#!B2f4-x{YJH}NH6aj$&N%qNg-Tz7{SKgPNRa!`j*QN@6SQo##>01UB!^I= zzjlJ55s;?Xe&qWS=g+Jh^nLFpVhNi4NLNFfIez;j51+JN3W7R&Nxpa$=aeRyuo%Bv zqs1e~6g%CFP>oK8e4c`E(uM(>-cC21^g|8s8ffV4G;VUiP~ZSSMygMp_v?Dqe6KJ_!kjnR6-_T?K` z)ry{WBc6CJw*FdY3~bGHy_OJ2-T;7;r_VIRV%G)0lWYZcar8XrZYF5YJXqE z8u>hGC0Q%kZ~|fBSu+KH0KCR-VhS?EJ15kq~FDZ5O+tj96zuf9!>6 zubhE0t|9&(B5UNFB-^*jT7_sg-(89~1cup*2$<)o~h4H|2jiQyhHpgx@CFlH5 z065!$Q6ix6Lpx`9^7@CSa)XwTf%oY`qQ1;F48Dc+vW?g}*`-X#8u|l2J!NnEW@w#< zvNU-gM-qyvL^w3#U&c#-(cUV&0zvV**XX?c+Z5^|l8?&42 z7uD@iQ}}M?K?xI0IiG7DYo4{ZWjji`X=WPk4p)m3e)auhg~7j%g`I!0`i1+42$adu zo%4u>uOd@VjsU+86s;tU1eR}!BsE$E==ga9ZSFxzNDwCLUhLLcN*@iS03W3)Y<`!Y zW6W@xJ1^k77o*}P-^iRstM7OWdC#@eYf<{xQgoIc%|u}DKOi`(-&87cnI(##IovQz zd$eVC{~xT;gYd++($d0d#CU<7D-?@NxVk9eL|%vp@E%~rBVnmC*31fiTqr`l(FdDA zS&x!tEGlw7x0iPC1N}YC=AA?VK1){j`9g4Up7W`kv_(wE`=`xUr(+~PXcdfXaxt5D zKQ77oh#L@F^34H*bq~*f$SsW7DHEAYT*=_{S_Jz!?1f!K+Zqt?C3s05#C|ULwz1!d z9GQ>TAA4NOzR3y;%CIMhX2xa$2{-$N3HPA= zkuk=$17^yj=6uE89T3Ij8$cj^ws$7UMb+vtdR~->oc<|^(@?>X+F=#q&UfYvPPg69Dkm-cPA!d)j|*LN+Z< zHF!9&MZ-@I)FLTgvi*;M_~VOO!%lfuEA;VLj6P{wC*C=sifb*chR7W(d9O;9Se1U9 z0I&E`S>?|gn|{?wklRM96I-tGZN$=_NtON*Zh9b`Iz9XI3g&6rblzI{RN$w|^FQou z5bjuIU&+#yH7F;1Vj{iFjfi;DwV)RPNOn0JJSX#CA4cc))g&c$*T6NK>1}rA;8^mw zYkDj*VIwXn%vT#!D!eU&ZJ;?-rA-dB2RGqm_%a=L)$lcW^Sr{Pe16(Cyw?^Wu%JPs zqfB|y@jgT4V3^}%QfLyV%iq$+23d;j()wc}q6giP%{LaOFrAr!L5Symr(<}L7eMW9 z*K`jy%|Z-$`wI*dm?Z#};!M#&%@)Von5$EtUZv#8A;|mL2%aptjw*qTpAi16Rq|C2 zm|2q6&V$Wbe0Ot6I&x_j*!=ci-rkaW@)P2Rv?G%A%3`E2O}1U@m(V6o($eet^=l9< z9Lcos293WPGBt&+a#nYL!P?4yw@UbBdeWu2mlmmFIW{+{l8M;nnIjoQL>YCc}WK2{B!mGE^b7rg2O}uGGUu2WY7Ot$w zFiARCUlDasH`o$eHOTVj&vzwQ8_mZ)sl@a6wopA1WwMW(kzFTV>Z2z^vLm?L!OvQ+ zqorBZY10RkOu4Msi7^8x%y;(MmdCm0jo*jPBFbgXlW%B-2qYjlr&uY!s-o}SMg5bD zkDZ?By2Kp8rY6--B@0lg6{np)i3#tf=IEfZ^igzpnKBgq#bqYV!2TN0T zoDh0oQ@8RW!84IbF)a_osaP`0++!6)zQVVRn9&3zwY6at2Chn^YVp6}P2W~^ghs7PeKjKVRi{YM4<1u(*ZtI@%Fz5<)7vG}hF!e$Y!cdDm?6L|Bm zd1~g)ZYp~gfmZef#A3WcoUdgY;HsSG{2IlQd9CbS8i-6H-H&%hElj|UAs8U!%vsDNM)@NcGQu{2u6?bNXM_?9clRisqiI-sSh+^Le_NO{n}t zb`xY%-n-ktwS!zgZY6oECp8H1(hY}RyCC(yOt;p3S7V2DMA9cN2Kln@cv9fRe#)p@ z`YOmmgV0caDCDPo5(Yo1Vs4PC>`3g3Y&n#oXS?K)4$7{M>BshZ-mEv8blQG{u(Gh+ z12rbX8{Y1#&ADT?z4^qzjC1e1K%91yWLpFitoV5qId}yC^J68MvhD^)hgrUV_p%raC&(kvtBkvx=)pgqz z15WTO5mfFLi_CvjkHKJ}G>j^s+Vg~Sbf&JS>ZwP0qd%m&u05L^v?xn_Sj2Y?PhD_i z(J1VO+b9}Y9HlPEMvR6E<>m_u9w5|c@!=}x=q!xq!V%y^$RG42X&%HIviL8}jGlF` z(4?fNHvQky7|#xOz4U`8cV;d=J*GC~rIw{!7S%!~zpx%@yRDcE1RhsBod%XtJlP+V zCcUs^@6+H)3^--4#(pr}U;^dok`A0Q!WqmilrI z_3q4EZ5fzhoWzMv2_P1RWkhZ$Ym9&dDh!KRaoelEV+59c<;h1jSkRYyVZ69t6XWzO zu{bhiOA=8pU2~_}jQ)jN2^{6{ zoyrsk=xpFpqnQ&~EoH8GoYbZ1-~Uu``3_i4xwE42=2vvf3{PM7@}t{;f2}^!=fej( zCqu8*^wdC|q-B74S94nlTUh5ylnq<`%zz-`vlmGb(O@};Mep~`paTjxgJVi2Y)aQ> zLA47g44G{&Vp0&|!58o5|H*KBu7xbU#4DbwbEnU9whMvcBMrQs15>a=O-XhoNLvG5 zkQgE}_gLaf>Ocm}>sy?B=22@?k8+>wy8p zDXFzI8kaInr8KyJ|DZ=3u@CS}Mw58tqyqbk@K?WQnG+GShBZKP&|(_qMwhG5&#N4B0y?T5ouf zA0M7m8UlVB*zJDBR%U;gqi&#W(TzgeWE?1(<7_F2h}KD(w8051u#AMvCx4Ee zohI2B46gBsgffj5zQiqU(@h;I5Dqxo8wEIvvlYZB(ZwLEX%9pTWa9SReIM27I`Hp8 z4@A1000EM!w{Vy5sW?At+9B(kZGSWWT$(R1E~dSP14AE^r`gK`ATa5Bbs0XUhyx-!W*C(a`XfXRS> zTA|Cu&ZC?fix_26rg;db)cQ0OF57_JbisW+XLRegh$)|rxVlv++F|x;= zRa}r9Hvbq1WzTZ6U-QgnCB~Ya#b-6)ah^lsiL-$(Qut#a#)1n9qUZvLTHbJrv`nN5w!oVlfd9}(1_Kd#VPNrB{A{T?8v*o&v;>af#0 zC;MS9>U6=SS3&fxDK#xL446Ahed_-B`@lGU-qJU`g_u=W=AJcDPB zx4ysNU&nW9qxuc0v3BW!e2enYXleTv1AyqOVU5ph#zM)`ZIVd14Q zHy7MV?!ZC$Zj>Tuu{rT!lfLfzToGCan&zQI-wkq&jc6rDYG{7`gV!~@4nTuKKh(lm z1RATym8!VA=IJI-Lp(xKjh3!&z_!W)*Byr0EhKAlkJe*%ra!?3nd{CC`d4xFISt2A zy!f*H%(QiUW1h0jniCl~#4Ee+OzeOt)}VC8es&gPvQwXT!we^QqlVU=F#9v9`^@zf ztrvrC3T75?q;GhWgQ&}*q1B)zG=$IiF^Q9ZYBCzXH_u&kzFZca?1D3Mm$?ARE_yn; z%N0Z5W*l|Ylcl=*!REg9G=^qJ`7!!U{CwsH*^KAow+KpB5|umcuLb@#Crp+DP1MIA z=VrxR{m9$JwS+qw@)n-(54EHL%UkbBiSU_MG=DZ4GxIo;NeNH2AWyN>54_v$40gWh zkH0B_YN>pPir~PKn7G$Itopl24K_A)1f48@9g?V(TQXY@=E9buY~T2D-xrN2-C&xa z?H-DrtABs_IlRjY#MyXPjQ={G<@vh{`!eKbft zreD2+vE)r0x<+bfiipA0>RF$is`n&s<1PK7s1nooRv+w#W`%PvN%R-hxbP7T((MBx zrm2y=9yC@S-#_mi<=^N3DQ`7GtD!ptUs}JR;7s{Z*W*T!$ILSNB9C3HZR&k}c$035 zCD^9`6n#fA-|8~P>$%h-a^4n6u+E~L&bJLDY^<8U^(JYLMI?`P{<`&doHVdu#flb; zT}mNmFU+rpU=6x`6HB0_s8KNZurX-|#Ck{Vu>q}a&z>*`++gaT6n+(OLCHe7!D0xp zzZO*q@mQGrr&e<^YrVNEH#jA30gYSQiJVgNN5~HuP5dOJu~jZ7PMNy#>Y=g;eZ*m&opt7*{Af1WV=q%wZ@x4(Ro zvCY6Iw`AL?%bbNH_nFQ~O7#*WP0=-9s_5$J>Y0v(3FZ;@rR%!X))saaB&Bq!CsmJ{ zp1W$+&v7mR-OZ+m%VN?b?8UzE(O}5<3`eEY)Du!FD^Q=38sZj-=Rq*CH(js*>BN-1 z6NRmtsuG)a-_T?p&Z=QNdAm>UHWy~G9ea)Oxz8q*2E0Y&VVK9}z7$r9H7xD9@$2LU zCmTw9iSV8OSbKpKMMzkCRQ$c$+RvJ^HeZaiOFUP7a`F~4xq2<9@&X3g;UbK43(+S* z`J)@5ZuaYtoN9Y-TWg+-t|oCQQ1h8B`rkp){9^7Qxo$YxG$S)vM{HBl#E8Vq#zqc3 zI2ze@7uqQ@i_}5r4tUISUgJ|gUEUfaQ=}3vp+O&OZd^2Qb1n_mLz-077)Cy?q&G&M)uW1eze+HS4Whqk;86QveN3keBk z%-e_TzGfvqdwH09>)Ts|{j0(=Z1x{;;q>%0v`C$L8_6XkM-E{_m4$q4JufJw)b*;v z4go#U=N*&i z_PquUEb?-+hQ>ZG*yfUpA2$;}UR{#6u;gN$UiMhLkF1}TzU5F9L~)hh(k68a-rSN< z+c}dz>Pq7p353I|<}xg!S-M}jT(UTJ<&hsW^!mrN^S^s3|4Q^9vk+j#))KOM~ z&)R(Dtlju7uMc;LQgfkv0@lIN&5f2@h%T>BS<9h`QXnl!3>WS##Q|GJ6@KdLgb{;( zT_%6G{z7YsLD_|64gM-fS$X$HPfWUX*5TlMm(P?k>p;p0is=C5ogghmU zYXN`Mb7TbSi5NcfTYL)&`TBc(HE|u(;ngesq0|L8hwY^;2`4`FsrJ4axcXA4G(Uko z9`HCZ>1(L#7;JD4mCu~tsV*#BSvr+Vbrvc`h%2D1|MT{KhCA0ke!ENVw)!?V%Khcm z^^|mGHy=AYWu7uN4&S<}zf@TbhFp=`FCoVEcFt6eMi)qr^&I2$*tqtWnL@tlvkLpV z3VQ96!m4azXn5BTM}4&Wglp}atIf*rS-w>9txE@>33_$+Sl-!9Dg}}|tyAer*V~WC zsv4_4B&c4xS5FG^;lmFL>H=FljZCc%+Quq_1W;v=juKxbmXRr))E0XD(4SQM>(VrS zAUYVqR>V5IZm=GnZTVD1ZvhM8`4k|l4sz5K=MI5ZP}V#fudjStf#xfl9h>Lt^cT~u z9p^kt?QbjgJGKm^G8jwJZhbW&y%o->)90lQ*Igy}BvkOSt0i0w`APH^Lh*TZumXR#p8ryi8@W+R-$z9jbZrNU1KL*Bi0R26pX zDkupFG>BwS)FDX`j8_D!E_Bm)dW9)-@)rTKaX5HmYe1YRTwnE8X`TMkd{G}QS#(^Y zcB5@CAz~NWy2tHeRNQbRixFLlh$_M+wG2gzKI2XuqHiFH6Y+F=m2q0KG(`{LYac5( zI~iQ2owu(k4&2-6wYo3rs-VHd{BI#&OE?r~!;Zg@!YI=H<=EqVN8z~S7^!J7wC%yA zD96Pmf4bwAmW1ifG?dDd)^8-JC_&*d32g5xE}RP(=AHdpp&?gJan|aV7&4D^m4vtn zB084}LXU;2nX^uat~+D?!a>?SKH)d%3oLXxM26wVgb*N#64P%X@;j1_^7@ji@&|I= z5bhm;VSTVGeXR?3v(&z&8_7EX-On|4bgK$&JM6IHlYo|m6bIh2l+USC4b%s1qtnh- zuwmH%IZddF<)=>7=_7~hM)Xy3f5#}~R9&oU%0-Vq5Q`iGH#vt+50fI*Z0cb9jDD<@ z<0A0K){YD6m{PT)TGbs(k~KAUt#28MsY9fq?GofOd-Pjnv67)hXLD8TYq*^c?Q5p- z=1+$hH}p^qy18~^%Cv|daP2J|#nE{_k}4t612bm>@CBou^wP}aGL^%d9rJpjK-EBB zN$*cW*>id0E!=x8w|>=Q4u3M<7NRa6LG!6?Qo32x5!_VlkR;K*K3=bL{t&nAACW(7 zQ~CSBG6=i(?}CO53<+MGE^p|&_3d5Cof75?%9iiHzBOQ)Wg{nSFTLsjP0+8CoF`=s zCG(S?+B2^D%{Frig3`rQu^lCO9^YLn*tuuy{5p68=V`mu75o=aTr71RZ&lA##t+Nb z^-HCm)(`bMVu=Dq%yg?HFm9*hv!Hha-OiMEfoMSHt+dt;ge+3-k-AQ+L-wZT7C@ zS9FCO0=UP4inJKcNUkwZ@5t`9`^%;M3Qyv^vO50}UO*O-P^El#JqyY|a`Y@;Y5`pN z=8I?jU&ALrAP^$9V~4cxS}?Ve%*NEAfk9beYJGARoPm`Q3kMI@#e%7t3Z&rdRKl=T zGT8yDB(Pi5A=wzIz~cWrOsb;_SYOyUA1EvYd{{4A{B40w_w7fA#y;u>f zWTC?;i+0xL=ikD9lO{JXZ`1o8{ff^j-Un2neKT`3w~~F%Z~E(_$M}w8iafrTpaMFn z4*rMRETq$3<9bd(CI!`E(>7oxa|cS+81D*x9~Z?kF~N(o-O{uPgVAOFBDiDx(j`R8 zs<{|(I7jVmGabJeX@?36j>Ob*o__uauWP&aSXC#1)Nw3yZ?&q$n}5X17iSl6jBvBi zl)jNC|6*(o62@W7#){=!IV7mpn^08f|0DNK&Yr$OC`vl$W0SrgcXMGFR(92pmTx42 z={LosM>zWJ&w{(>hhEWc%KE7$K2^M%ARyF}rfv`n8D31QuoR>qu~XU!pZ&9G&0n?D z)P0ru*4Y|1tU9FE!(Dhw;uGhy%+nd;wKxQSyv4!3!}wBI5g**5HFc4U&OtZ{__@jc zC;XD{^(2=r6t9CW@lePU_Td7PFNV4{Uq6)ITpRY!9Tt_eDpKvYP{Wp9atb*REU%7+=f9gKMmRe~n_D zU6TaKyQ@9CBeIhm8LdQer&E5H0Iu!<1K!?9S|_6d5m1AGtlAVYVA#SUO8|!{Osy;;1SCMD5-1|1 zNC{qQ{4=83_+Q>O{xUnYq1&>Hol_ zCuT6p_LL^^zk=lj-bJ<$OqP zQKK6I86%a#EcgF~)F0{0DJm~e=H54q(mz9};uloI7N+$Nx6KvNaQp6iHgOzT2z?@| zns12ods4qphOg$7p~v6|dP1W(tT8lb%V;Nsub_v41aq*Q-!jV@LVOEYH`?en960@O z>G2u4+Mgmvr-kyE&9mn1<99+(DZQiI?^-4(vL7C|bCd>Z*0v((O&e2M-=Jx{pIqkU zGxx#0T6`}%@X?+^vh^99S*ncJ7dXFoo=zV&&j?s>?XEj%E6Wf!Z=bR(!TPbyQzD{7 zzeiR4N96Z3QDurw*$;QfgCMbP3)E{uhXy-!YCh}WOQR|U{;m5X_XR%-!N(~O8ScEB z`v!*&T&w4QTuSw{v+u97j%z+UyId%UZyK(%<~`4r+mm=@IT*G8%L`9BdmHtwv_-21 zLtiLfNO9wT(Ph<@Vv-Q(ho+1A(MKC;$Cl&43J*#yEE7UynYhUfZfpLL%z@N0Q5OGn zQ%7x(eB933KypzQo`OCs?@n}yzgiyL7kzJJasgY~IueocVlyr$WlHnh^2C5s! ztkzxA;D>u+@2mI@SIv7bFoULzSG-bEdNwuP5{O@9+Q-?-GIgORo64%lt-?t@auk+N z=jJ1B@@xDYJTmPyT`++c$*l~S(;4sKxWvVUA!MmJTjcH9ZDQguXGk+=QDh@|3Qzpq z@L&Zkc~tB|^Iw*g(?-Zo<>}SQITks}bkHYfp#_-)s{>xH!jk{}-+H`PGaGa?A78xp zTNzd}ybQw%dlKhPuumDzA*I%GHYYY^o1rqhPh=!AKXWsW9k0?b0jqG!=KkFrx4TEs zQap0HB?2!zj?v4Jl z5^W7!zJlZSzsPc69`z5LdoUmEF&4OTOVQ+AftpK^0`Ax=?kFZ&Y~ztH5cF$DYa&#( znQ*G~e6KPon=aRpTjxp)^NiBBO^tXA3vbOD^3V2s>=xSP7)QXkbvN<}N$yqp5pBfD zRF7)K5ZrB)6%MCI`31yJ*JGs)bLA$_+X&9WQf9u$id7==-O8qtThDRp| z=ypKj9J&vlZi!1u=OoJ&R@ABB2o5Ng$V<-G(F1hymCMi4-Hj2>6*@Y9F>>G-kx33iR%9m~d_6*93kSmo~@;k8Sznyb12J#Pd z*imOsL~W(b+c~XVXf`m}4DKN`+_2~@a-1GxDU;Gsz=0slBRebVaydRZFGzMv_OGbn z%sS;@Z4}wWvMb~T;V4bOIb>UfVOn)jC3+Dc^`58%BMRik1%h~%QO;GYabtJf z0NRt#>J;Gy$x-ABz@BrR<}qmpalS!1G~E(9W7;=>)hCwS{l+nMlFO7bMVvWAO+eK- zJ3Z*hy0+T+75DCf1u3zvuOPIry34Ub@cr?(c{r;xM}-Ma3+25HDcn>YmQyjmeOx+G zxm2wNE@H^sm^vj6e!cuunVx=-DR!K{Cv0~Zl1Ai69(p*Q1fe1G;-8zmNgPhNh}AK_ z8CM1Hz1H~WryM=_rs~mqEEo5NaFC@IxtFo1f=PC#ZZ}1$POhMt5Q5v7^>ax#zY#v~ zyou9q*dLP(N9flW3(&rG&p0&pLv-<=TaE*11t~8V^$o~gAcwsYN6HbkQp=c|lOfgh zj0oE?(n!792|B-@HI>_6#3zGt+SPArF%Cx@u;P(f&LQ6_4rKzB(`dzu^(SQoXlCD$ z?GkCdQuYHifMm(imGEWI3VG(8pj!!ZniOM=?=jP|p4us+A^4r#}{yC-0-J~&m-&7~&r(urU5pS@dAeu<#EjY*cNCxEKgzo%RYldTy#`?UJ3 zi!4GuKEt&IYd2$ua3d4HJGn&7ZT?8&%p%Ir>S{QNk-6CLwc=F!gM>HmOl@#Co#ka*{= z6OD2l`1bT+79{6N#ew2-K?O7VK|jX5yRcI~gu6hfXCQ4sU$;7Ov$cG|t^3np1qzXQ zgSlKenQbIUy`kIJ&%f^4JL-j}bUJ=i3n3CHGI~G8nK0>#3QlE4NB)g+_$pXc{;GVA zKK*B2&1y}E@S?(LQI6v~&8Kqp|C2P9lX6yc)9FXrljtfh=|nPZ}Azh`I{a~H+xHiq&IqZE{-uSIZ@3S z33m{w1HuvMJ1Wg*HxyvbQI{wmfviR$@X%r^yyub>Jns1)L0vEbCZ~eqNO^HBlwi%1 zby^MQd@Iiuwsp+HxNUNE&Gr=0mZ16B^C_kWR_FhTdXt)ZGO2eohm7GMar^R*S7c;- z&TKL8bu0zD`8A%tbv94yInq*YHGc;Zw@^X{Ynv8OHKhnZVQvW<%VtK`#*{ImtMa5( z#CpCF3O$Fm+inSV2MVM~Tq*D7l?uTn2p2`OVeG~JcIpC}sQ+S3sTh@|4)ToRmpXd& z;;8~@+88`sKB?J`5M4G);q!v1liPw8>vn>`O6r!F0KJdJOV`x?VvzjdbzBZ+oDm-Q zV;=V(48{)UyYIu)4;|6&-i;(~?F(Bo3ClSI_n+dE3}I|{iW)5}2Q0Z6>e zfe6zLNje9|_syS2K@|?Mazjw1=s-=^e>l~ciwz_2cbmkABB{p_^nrJYG;=o3J|_Sz zO{zw?2{X-UeO}U?CR?h;lkyYPKnt7OiTyvG{()vYT14zRP*|)O*I}>WGxPncG|M=TE)Fu@hj}6xOH>zOF8zhV? z4o#Wjl;<5e=Te5h9;+91Zym+QF3oQ#+hh8X*wh-0LtcI2!by7I5x(53SmE*HnvOfm z3SpNsEU-G4oYS2!9Ct;%ry?VO&C{SAaVP}A`a+Q>$O@_zeiaK+2+GNXrJR|x(OLpA z$zdrA;v1)i#!uk#vKz2ivyC&uo)?~|R+h>iCTV2%{&vh(RT;P6uwFMxW-j5IS|-Ng zm5T)wE-0D{1edtNu^W5zZ24wQKwZQzq0sn@scbySn)-M-VkEgUUEc10aesb&|EYiJ z{CA2I$IcP;?d%_6vZVY0OhhW!(|FkC-`0EP>drl! z%OEunbUX{dg;1ZNpFmo+BX=Rb0_Drm^71RMolw@WkaR@GhgOyqR_J|SyikIrg>AL9 zhRurnQRtU;lxfefHWI`enSi*ccLOyiEDxoJML%Fg?KWWz{4WmCGiAwPiG%XZ56H%!T^-VeZ z@tL`gPvEO_f(nA!?1^)-*e*zETTnozeTq3xtBvWXX+o_aSJQk+KP%gHAkn#&t2a<| z(U2t{vp=NNaaBImW6<0ezrDMlqVkJ<(Sn>dVOekHs6yJC$rarkM)gG%%XO z)ua(a;T5B`h&6T?)-Jm!PSHZ!qpA`$4?Oup5zDho^bG?vp_{{l|CAjLU*a~6N>AT` z&!vQkPs>IY60V0=8AmjnC3~k>p>a7&iuaUZYZT@1Bc(T@MHq`?oZpMjQCgFR-X&+j z)`!keJ+Q&dRC+s*iVpN&C?eoN3Sz3wmWdnUmJHzx7*oT`4ddQ76ZOnLXkn(wA&5Zo zpLxh%s63KDVVY}ne8>yMDU!Y|7C9C-xLe3d8ucdZ@A~|9A_{KI;Xn8p58yG`XR!FR z_wcBq6*z_EBoL**G9$Zf%(i#*fN%IkLet5nCgpUEeS%_$BhepDVDXK;dQV+A#q;f&wOop7<=g~t4!W(HXgWwaQy7n| z_?C}W{X*jp=-Btb`ZIovX-oKzmse_)7LOaq6Ib3Z-^Q&M|Jw~YzK4HMo0G^5I zngF5Dk#BDeZ|v1R=St*~{KLNDPk(v@L#@O*(#a48f>px*v=&}`%WSFx?%GXeYdCC) z{L60_V}6wkeCMIlrKRKL!n_Sbef2mSp}C}H$*WhW`Y~?>z%(9J1S=}|Vf4L6*o7nk zX)aeu+#Icc0!F-dD+oEf8+^P~2HUtvl8?^LN1vz z)OFMUNHXm+6v92)Il|K@600xecTqIdlk)Ke!L8}~2r1&oRDC8rAil*B419MFG{-Ny zY4nhT8KwXqTZsSsY#V_@cSJK_-gdM5BoZG``tM0_of+s_fYOfPK?fKrJg4z2!`sND zumWs~(0p^kp1q*C5_66ZryOITy}7oHVw>8SY*&eSTlXN%Z6-feXzJHk5Zf1xARy+F zW<$9NyFuZGD@PXUF=a1CQTeppD~gAr&5A!LqJ@l4cwVYIrRx8lg%5JQ6H4kRT?{57 zrcuD*Q9AWgZDk0S!xt;9Sr8{^Mp;b@$MI>qr$BzS)oiM{HT|E^47yA0tcQ0HLfVGv zv8^R(B3r)b66V(PBk$+aw)W+-1|Om=$)wS5)}ulGAmnK>1Cn7xkeoL9LmuWd-hA^x z#O}LVFtu?{n1EG;39U)rfmV&KR9o!P{${GUA$1g+NLeUuTY@rRkIKNhD;w-1!?XFi zfmC<}Sc#fmjlK!%;Avn4g^gu1!Ac6HB|VU_9ptEstcW)JWNOu928o|MZN-)-eCK5chrNvk8{blYU8eoha^!l zbxMgVS8i00YUboLNFf){VBNI?=0Q~cc0Gjb1=AJ=E*{3r`eGIdK`RpTz6t$_=h?Nh zXul*iKs%z2-`qB9D0QF{O8rc8{*a}eqLMBCl!x60OLu^2%&1hEpK;b4{Qi$**?YbV z!WniT+0>X5uHBpX0Dx#u|6krOacVx2As&zVL#laUiLG`ut8n>Zptjhh`~UveuRlMI zgp4n0Gt+5*C|vzn8n&;(+Fj>a)ZBRV|Md1*D|d(oD)|0RlXps-pou@){Go7kI|Y>? z?sOqcrgfik*3{?!k#y+8A!2XtSPHU;mXO{Jg#L%$Rx6C%ca9vSJu>A*zj|7~`J6z} z`V`aa`^Pv4?wET^!T*LB+^Q$m5oW)iYy%Y)Q;!89XzD#~^R_7(q=kty9fc=wj1^|)6NXCNf#36@ zinAkc7~he=Mhml?z>QxjMW6g~zG_oS&?Y+=;JnTjZ0e{=<>A`|)r|A*{@^Mal~Xr= z$;-VJJCz`?LNNPl1A0sm3xJwb(x8M@fvOaCu9p2X4IPwYwkeCC0i&Bbx`MzRx+*Ff zmsQ`(BbloB>#T`9Lk#c%+XYI{a}2@IA=M2a%~WjurE|oY*{Tu=FsI5AsuuH#5(wQ5 z20lvoX(*oSFT|h;O;jSP;*{X?8|5#DDl&fKOm}6dig&MluxQ(xpXyYbl7|8HRJpB- z+-vAE^MAJqYkTmgojt$@nWkVOw~KzFs`AZLRsJ-R=Nm`*E*$=Y>{F?WgB7XK}ovz~= zWv;XTTEQDFjU~w6oLI1?J~3}J+uN~iU%h0%sh_r~&+%2O%yg!ys?fZaSFVXy7>%n2 zI=Sy>kI)=w{S%>)EnN>k)A_V{J7f7$)jh=KG{nAD^vVXyiR=iEUKdMRw`E-OcnM+R z|I*-lUwIC60(~i%@#uj0pgxheSf$xz&0lZuUv+(umu42(Vk#$25hgaJ!C$f1NDTe- zhVh=PkB5A9JO)I4-2s5{o-4;wNrSH)(%3kU06KERc+9o&Y__~QUN=PJ=kcb%cuQN- z;IHzp2Xb{bx$9t?CPc;n(GFX7v-C*S7wk4vpz)7O=R)yP>wtQ+O zpZIK3r28ul);|-bwXWLZ(WRoGKA7Si73+Jy_UX4L+y3*?>e9Mnvs2v|L60_%kE})8 z4H2R_xp034|K)dS)XGBXdn)01tC4eq%TDW#bNNXp3uj7}i_56rY`G12mC?hdQ9mh6 ztb3}PnZQ70WdAGCq;TflEic`>DGLHzQtCm?G_*3r{gN?2nO>S-;f(HbaUk1FT7!%x@@sriC+?FjnNKPt|rmyfB45PA_=Jt}V zkYCfK{CyfE{E1SaDrxA?U;FzOcR5SE_<@6p>4jf2RQbLN&vp_BFh^5`|7uWH{yx*@ z-n!zy|B7PmDqFb3zXTF4@Z6x_eZXi3Qw1v)iXL2Z z-khLJn${jNbI?~QF@AL{U{w-BU~;CrpumsEBA#!vyUMId#M zP5$XOs5+|$0`scquKvaceKVCqIjA(hDj((iZ89M13}>#Lx!YiP%HNM`@s#iDsWfkY zg_PCzu}16f>%1bc)%SfWmG3*gE?rapUbnAk-I18QR>32WATmB+CaOe!_4;3*gRXzR z`1PM3%254~Ij<5~{rcDc2HdCII8nua>nob1q^{(jI$-Z()9oFvc-POKn=_VYkF6;Y zB6elX*;u~fY(9G~W-OwwuL-|%TEnygzxg)d)!{Cy&MT8z;9B1g-gQUm2z_6X$4>@8 zKe$x)PMSzc&`dtC7_UNyFGNHvyn1BP^rp&B>KSP|9UlNjk7lMO^?_3`ufl3ncCG7s z8u}P@#&yMHb#3oE|JoA=DxX~rzpYHLfnx##Pf@{jnrTRXSlMe4J78#atyR-iQf|D| z1TK@`n-%upwRkT;bL*yUN*li2@+~)(u!2=ChNDapUV9ox<(jkgDdoaT#pWYPTb-DD z3ruo<-44cUPn@W{?~yduu_}#m|9)XBQ@!&y9|yl|RMN2Z(r-==_}MTe&#bk;0;R7R zYyVpKvQnW~-=we#!uNtT(VJ%k9;;KbY4ntuvbypn34q7^zg`INZoQ+Wtf@3UeJxr< z`O)SN0cT>;!CfZ5!E_)=6Qv|o5z6`rE06y=n6T1=jr;z;&bXr$TlcP7M~QO63lS%O zlf;WLn-3}d_W!5#cuMsWsC|H~N2Nk7`+fLZDvWy0Z3bV&8 z>=$DrhGLS$RV&B*HB@XI->4n0u--pInAEKxPbB0NVmB`o$c|}8qLe2IyfRMrC9*1Y z!y2)O3_{DBEas@aM4O?#i zMG^gX7($PE$Y*B3ry|Z5u1lDe&%L|1^o@?`XB#O(fHOtK7aC3n zr&abs(oe${%Un4fUtsv#P2N#MzsXkt>v?I}Xx-};_IdhW+gI?-f}-f`)8D~03#97T z!Ip#Kc)slDiRJj+C`BGY6iuflMJ&Yr3HnCo`m^`B>xn#djywJXC9g`BYy`E;U;qL(FIkB+UNGBrQ7(KXlzCe6P!d$anfGsIASfg{kQq= zVj3!Qx!}hB{~iO!fR5=f_r?4G@r%xlzv1bdEsDQF+@^K+cc|2ns7usoEcvAE7k{sv zg=_!l4Aq|uq3K4~vHx|_TSfycwO6ZM(%wb2^P1lvxzcA|UsQnFfU zRZ9-Xi;TzGWsu>HamVNk#ry36%qP>Zzv?rkK zM&I>QNCb`_6nMEF=s@;geMV~{#2TdyH{5X`y#pHT^gR%NEu^vB<0gZ3F*MFcK!>{y6 zQiJOxr4-tGKxvRzKNKP-=j`|xVJX?1PpO@Q%Bk?MrqS5& zpUJ!ubBv>+V!PtD{KHV}s*c22z8Xnvt<+=vrCb?}4C_B*BOt9R@oKP1)<42`7 zVWYvVtqN{axQt&*aT$83B4M*>25fH39KhPt z#RLNUF_iAcZAA`$PDir<@OWU*F@@WTVH{8hp_`qiMlLxFLd&kKzh_JDADTF~HL(Qo zEU63zo9li!V+3Z3F;hsD;5JEYVTxA;D-^|@ z*^g|pKC?A*4w|UnIhVw7YtKGq@CW!yxR6B}(M58gP;I2#XtM?K{1<1J%FFTfId&5B zugT_{O{~okal4Ki} zioz%P@UrwexNb-ge9SKZg&_B_@-b((Hq1dfvQPc@_iK24SL8uI-|%dbfU>E0|84W& zvlT^&dysNwKCRUDyhX^-(67*NfX-*TbB1UoiI*}RHK!K)fm6mMx-+AyCT~C)eERK2*Tw!s4&UkMB~P~w=;NqPtV2;&-oAJ$Qt5qt~Q z7y04~ZoODtp#&@4xl(@MDhaR1K!9N}m1Wmk7%sa_y!96B#kEdx&vum-V(z-58E%%6 zmR%YS9`K-=fpS`++SFpBzzz@SyM$S~G|K zWVbgD0O*iIPakf=F)0WaH-}~M5R~KGt*Jg(FlTT;}8&M#At%lWs!aT$$Avi7QAT9GK(bd@k~PD^|CwaGWF zZeCemp1!pIyJO2HCUz&-aonSabV%Cl9E%uuisSxk4G}8a)vxqw>s(FL=yL?ZtM1mk zn%FZnBvrR}3kdm&Z`c^&)P_Cpo>3;cWd=^GcCbMM{wMZs4)hzSXFd`C1bvfuB^-n< z!gd87SOwx7|F|O8wL7-4lr!^}-ETCBpVvZF;+`ZbXt5h4DYVAFuuT0DI%=E4NPfhM zC|kxS$J(=CI6Kn5LrTg!nFTcWoW2uM0*e07nN5HA5XPZkP+QUGFwDKohtf(7Gq=%{ zdeCdb3LFTXY!mpUPIb---{m8xqdtV$&sA4oYSqC(tO)uz8^B3Nd{Bro1^}Ak$(S6B z3vt`;NSu;)<6ktUCWu^|Ua)+I8Ys(KC**;?b|iqp>jmhx4`3&PhAX~NYm~<-}%u}&hvJ&YH;9!>4W_E zdISeKT+vf=<2*B~N&bob)p&R6KQDG0HvaVWDiZxR_1ZY5lAZx&jT#B99C$Xs^SLYkbc0I434P+JKn)?v*lrOf+{3zgl@wTi}((3W~7(okC*D==ONl* zUy1fhY`zvZ!x=(17Y3Xyi`M64M_oc?IvxUjz8X+;#|lxSu)ceOq&J~^_xSG->Awv- zCR%?m>>%}GHtPcO%EQ`9!7-+yeQNpOR0!xY)N`$evQ)N@1P5MukNIL>TrH7BHfJ2O zr6OC<`Sg>CC~yLh&DTm#Ti1ICXt+VrE%`dE1?!)|yC`lztZhwg?9Wv<`TgI3yj0dI zD^=?ZVp*Lz8{({U(Wu~YE-ASH9GBn~Kp%c}!AlC4n1fk~D1_n$31*rPi2gGfIQ%vHH z!qY7lfALm*qO6dr(2j0w8@9AYmgj2y=(&*=)2?al zCN1qdM?~D6C^Hk5Wk~7Z2znsS2oN@NoVX)CQ?y%Imc4Dh;RI9*tm8Xnd0(Y=wf6Pm zeYGh)mU*U_r{PCp#)Trbm@}^p7;fGJqUkMg6wiar$Ix&fyNxx80w*TsI4rL=gV-g{ zws1Tt2p*JUNM##)eJ>@yL(7RMIBS1FVGJ#?r;A8OHV)c0e-^bwwU# zA3}Bg{g6(6vF(5MA53g#4q@iDdbxDRHD)EWLm8l3hb4%sOp#M?8xjYX)u6gsjMxa9 zduWRR5DVy0l`MLZMB{`({2aKYqtR;h_>rRo;_74!fYdo4b7HZESjTONh_dUrM7=wP zrXbWld@Z>~tK_==s*Jj-gzgR%-Wf&<`KK>^Qp>W6Rk?o7>BO=F04>k?Eu3UMG@s?j zD40>}n|XscyYb^X=c-@}iH?3Nn|2 z97EbwY{2oPb_Blf7In(MK&lr3LK;M;;RteihBQEq0L@Q=YZvWny(OhA(zzO8ec!D} zQ~Hwce9`0f)vc3AwGDll=0?mwvAgQmMQM*axnog!XlJ31V5{_5AL!s%@Hl@?F1Z}d9$*;- z65$MZ;+zJAF4dYb&%!te`v6k-8+-Q?E42=XM>|W1x3WA>HZ9(4JBOK-nk)3H_HewE zNy{NNxG&WYX~1S9OlHKTh0J_}bK^`QZMP!we#-?y$xP!@qq_+O%6*)1tqsk6Mp^=W zHmSH9|EhpjrgT%bgSlfx7qY4)>fc_nJcI8-cw(}KbI9ca0k{|b1LRQf?B1??^uGQf zj9_1X$(>?ywW2WF+JwdTWVo;y>>@)!soFbz*NCcI^MoyvpL~BXCmsur}R> zgxRb=W!`1p1K9~zO=E&CPIZiO{+TX{bZYDLRguoKBz@*B(9KdGkx1{0#)QvHim?Lp z{ji8G#V55+w0A+T0*SpI=$%9CfYMVfLXwcB(d|=QS)GUo@gjS$Ue;^qN=Q z8C~gRBO89T7csl)*3b*E#NeXq#kPwU_eKs}y%mJWIL!Rcd1*MGR+gy2K3xP!Hw`Y0 zvs36K1$H{;1OTD7c_h{G&sCwOqfF~H1y-F_(u9+r8*o5>P*nb;%rb?@4`UdJwhnWKA~!Axk=<0QP=w7s&xQ;dB0$@M`rAe$;>M>tGG6EOaG<@Q zN=fFgD%(ka%MfSJ_&^g2uW3wG+{sn~nG@oNzPLo{06^yEQQEmer^b44$|C8%{pFhv zv!3`6S?FUqM2>k-SOAnyj=8k-ZBC(n!{W{6d{)XZgDmwg2xdMX6t(u(5AtvMVMgNc z^sIPw}W34g``S17TKN~B|oU4VnlE!30(KV*ULTeG!fl?`J4WjYK!JV z?4Ri#xI14A34K_39*`1mWXnHeiv9`o_b?YbjZ$XPUA~?y^R^9KKj^IF~F!v9m7skptx z@%Cjcrk@v%x~qV2r!4QWe)AsWkd?_6`$y%hJjUtQ%2&KT#kFm#7w<3k-%Z7co?Xw0 z2Aj*+E2<2&sbC}i-%LFeEGP^>z`x=J!W^q!=kLSkfU1B$HrDH@Nxor+3|!NDeX)nJ z*u%rGh*Bwf@A-sGzDSjZq*|5XpHhg)7<=InzL{&*m5jtLDk1oSXz}I(2e(X zYWdHEep}AL)h7`4)-VH^o1-;6+pZSx|9w=>DD9J4{H0NzIXIY=qxck$GoqCadYC?L zrp+NP4|;Tli?kTMOd942qc?xTeP$;=Wn^X9KyeXVdol%mt~vqV1x*wtaTSN;Uk1JS zQc!feRZ+Df#&?RY0C~783dL$=!Ghefa{Xq1fudzmA2dJk*e~^+hiLLo1h2Ro37|E5 z3h6t+UIt`ipczuCwQa%H(!O5p0A$A(J>-i<)>o<|SB$I|sw94sKFoy&OjB@eQm8Z5MX-%EA+(kyW@h|@0X5bE8_ z9YYj|a_Jt?Lhs}ddm%so&?sRK3ip=8(^t3XCu=q|{Z%NpuxHz}ZELUW!Q4#(>=}9H z)RVQm_PM#jL64W;)#X{X1Swh1sxa*6Rwf9y<D7tJfP@QwEN-6yD%Q?-xW3qsDrJxn zp)+;BA*bXefmVJ`61D*7Ej#Q%h6uZw5Sj*U@M~ep+(bOpW@KxE``y!c>ZUP;d2GDg z)Z4~gnmeX8<=_QiT8pbF4ELHqm{m!tDJA-^#0NNiW)iDy%<`gP2W>asuoVAvZe@-$u{x=)*r#Tg@pqo2 zX=kpY777_Z<35!&@ZbL#7Z9hE8BUsLpaZ)&%Q)|=yC31GOz~fxvOs zGo|lZFKD8#uY8AVf5Z)o2I5IUF8#3j1CSFCkAfh%0z2P~)? z^7tdAfadtA%lrwsi&C*lyjrp6d?FyP@+<~8OdovA=1w1E(z+fDxs5#&zU0u8)gOLR zjR!m0KaEIq778L#6}wi(4HiK=z84>@d~SDxOz(QW0{qVrd#48hLB z!1iv-vA6J*&qNASWNqFxx~aBinAJ%R7Ae2RTay%VLwLyo{WewnsnPFA{Edl_K1Dq2!haWF9&|UX4#Kpb3F{Az8Ux#C5e^Oy$euk_fs=s!6{+^S0R}OdH zGF6djWu~}n;8VM@e3;vuAmS>XpHIniS}LY~$N&I-TKx}=Ptn?gs}lT0cI5YDJ$k0E zFMPXl$8uI)e%3_|E5X}{LJYRPbVGlr#;Ba$Q^M0{FZF;)Kj%Js2b|f#u zs1S|99jW3hu3qNgAg?9jMK++yEJ?DmMpe`$FqY+O#)`kRKHK+xo^7g3xIC)#SOBh0 zP==+=DmJwU5XvRrt+sjoy&8y z%n+xA>rS>x=ejyTo>{^R(4kftF~R{Y%=JdjzsR|)*J;6euH{_c^BrDL!^h^aO_!9@ z!7nsX96(a@X@;00G!eE59^sb{<3I!d(XCGaE~{`(w{C~oYbn%Nqwv&Uf6ko@ zpB{m7;%1Svws!36`TC* zb@GI)r;E__V%Co>X3L$uIPN0v7)9b1F|_#HI$6eRSRS15o}nh+jYlnik4xDLzUB!{ zglCFc!P#D`$G&PEPKzg8(q_=vprQy2^p$+MbYe}3i>|Lr%LtlyBpxssswxg${zi*+ zZWj>)-lwzjs$ToPX#M>)MAoWbskH9+YMUHRe8D?ER>-uz=DpmZ=eRr7{HF8FB|Xnj zmc;wMP5fkI2a%+&i{n;B+82Uq`|RtOu)}{XXEuMSjuV=T1-+U}4>hB<)AX6JEbZp7 zOi8E46zK7#8Sbj%irS}p&+V*8@je#q!pf7Wzh=e(%r%O&j{lU0CrxbS-oKpusGDk3 zCfiX#{kxZH9#}nhMIJ}a<{rnDze^AB(_xEjd7qLJ^uCWaeeN60eJ#1lDg#LID}LrB zS4vG4@~8`P0SJ=Pdmtr4YK(=A-vo` zWhK)Jx}&5aF1K-+@L+$twUp@c{c-l9EJ8Xcuy<@NtG?5VW&!4_QUPv_AoPY}KF7d^&KHfuX`~q;~^oB43ntv;O z%sPZ8`EM(drBtM7v(t~8uP3v;?RA0YeChFUm1-UZD01qiTF>QL8|lZe;glw$oP_Bm zVh$_rrYJ^(!SI=Ck4Y_?UQYh2g3fxEKdq~esHQ=@Ctu7&#o39QIdyW~@2gv|Y5=u& zb3-6si%TzIiOShwR%EFwyE4T3@C-3i?y}4`SS-?`v)9%)uL*e}yl)rT2(#iGQb z(TGE2Bc{WzYdjHK?*D9G$cm=+{i~w$)!>vmw*_h34J21`eUvNp>4Vrmg-jKrDa^Qx zk|*Z8b~;ZV*0h6sOh=@1j&Zt;K7?);)-#vgkaWleOSFkLK;-#sNO?hU zGR#>q;+Tq|4FsIYhSI%HTB90bXKA~-b?0r3XR;lzpDfi~y_IsA?vltLv!^F2LE-Bx zzR%p<1ohExVEPnmD*O{$P&96Ho?lHlE}k zIvZvL5nDR&{Ph&(8lx#q^?VCJ<$`_1QaHE4#c!By$iEgr1>H>EKmQ3U4;Wp>hJ^{} z?xj?)b-dY$PSbs{($uQ

^s^_g+{z-0eW1)1 zBaRy=R}(NsG*&>By_auYzbPl6YVoG=fGIo+^5J+?-42jescl@)Dc5s9q3BjqBd(%@ zYu6>BEEYtaMWMQK18toieANEbj3%PwQx05aGt>3J9$HX_`*DOSx>5rOV}`6SKeP2j z{OJ6OzMW^k;~8X6!Vy7I&%B6Suehqy5T4j`+*hi_oY`jLy{Cm(ZBDF;GhkYr2z@|v zCiXIm_?QQ>U}O&>j=+zeQ{3P@OsSyl$r_cB%@yxln((y%xyW}L%K4!2u#)DNdud;; z%#VQ}mPLJTeMtUZ>T-=TU@kr;E(n!w5!B1Y>Vq+Up)anZ=y95Q=fI5$9!4gdfu=xY z&)#F+4%tSEfJQ;mYz)1#Od*v-T`aFAEe|a@^!HD=DO=Ns_VMiF3nnuMAyPzzpil!d z1_MA9Xi&sK_KG>Nh0GZW@Wp> zoRZuUk@^>JrMAeTf)-Py!!%}Ig9V{853qUXKpQpAPS9xPaS;W*UzXfcpn4T-HnD{y z|GbRKlFx8WB;?Q50=d+&0K$Zs4&D7&Fo{C{yG55xw14?VOdp-D;S7A5u1bgK$<{ z#?}BL3h45OqLllHSd+$5BQ|S!h;JpB8Im1Z#Zbl7A)GoP6f;`cO6Z(9saKB4P2nq1 z5}>^GUEEvQ0xCYl)TJ3np40OuR4s<0zTiT^ew(~v3$;G0*l)l1>U2q6%q1wtZ{a!L zb)B3O*+kT{BEE)hWB3#QrUHzzgA+ujZO$lLG?Y^>mPS0moWw?K73q=-Fdp6NR~2Q* ztqs)MHgBcnMM>Mr_dGkQ$PSVhozj=!RAAAZr+SLrsqq8J4RFV{y}`qlT#j+=Hkg0m zG92h2JP6@=?MBtqpq|;ZF((L0dFKQ6VbChBaml2c6P=tJAcXiI!=-@yWFK$nALf!4i&;%%SEec%~}=*VHp384n!^lbYIxZgg2O$yNDej%7&hjm35p7aiAwwP67W5K3%o;ZwrsVI6023#~@Xq8`9M}I}+;{omo%(XLErBGcBMAwV34vb< z_ql!plpz7TQLF<@nSEt&8<+|dV0F*{$ABQP#vY0;WAO{VE(~rF>oc68L$k(PMrj}0Byii?($BizGzn!-=LhXIPOcSK}{b8a3pIlojGlG z6=~5h9f7n72}dew%W1~o9xW$C84+&~Vzs_?<=iO1tD~%IaoCyZBzmV++>37k ziiTOO*HF%a1L%t0Ck0p>K&5Uq?x1AS5xyzY3X2XWsYCq1-@^=qO=UmD@G`j?x&x5y zgKQ2ti)YTGGsVXdTauY66_c%O=BcAW@a%fQNT!(C;@>K6J^@;J{ujV+>cHQdxbs0e z%+0ND^DmJ{;9HOvm7hUpo?NIvXz9#dZB53LN{#M1AeTt~0~#L>hys^8?89w|!B-(P ztZR4a*DlppQ6t|tUluLvrCke(XR$j4(>&4v(Wp}kW0~=0SW_Qk-?adB*0OHl% z4J9>jMTjzly*w_GZKU2gyY*cp4lp?5q=xnp7ml5D?@@C}lpok}jy|C{kCrCfY}tI+ znov8dM4X66)H)7nVoWYY&C;AXRFW+CBn8z2{8++L#RO=`&enkNZ@>4pT=j7gPmQPa z5iWOaGSw=9+MY;UgaANWX7mk`f$~DS2b*dKOIY{ZPA*71D^7z>hh&>hM^g5Z7A(R^ z>2W8vGB;~HeoKVTK)9ywP^co*ywRX7#n?21X4>InrxC1>sxKkIg3M&po20|poRab-nYtg4>GqhA1hE|d-95H zbKws%tTC+KVX!UshAdc~z{%%Z zbQr)8QYwlbc6LsfP9_9W)0F$cX_z-la-qLGw*_j8{Pv5UG-qI%+toiS0P8>ATx#Mh zgumBec}P$Z3lfMM5vAl?{x!Oy{?j$L0{xUN-2h-_vVUUsN=7G$n4f81@x zIz*sPpcL|j0UR*~pE3_a1Zn~}1oE=f(1ZmdY}(Y@xB>2nnoaXLSr_r`4im2H3|;$@ zYXQo@B7fhR(f$A3FCB>`2=bReD?%url0R78)e74$)s;Dnfi~@3EyWFEj#Hr9EYDTF zw5`8rBAgNHTj^>d*Q&m?WKO&)PR{SV=~p3<>G^RHgLI6uS*3r|Y=DARYsgb;whVO$A_zSpU`hGaX4g*ir^w7C$Jtlj~XLzqqMQ6*%47m1)8gO z&i9{**{)Dpy-@d(5vpkh1pgnTuwXl+0?KoY!`zGmeEo_{klL9P9xP||o1^XA4gDneFI5qdRcaUB2e{KENEc#I5Lc74@62bFCkDfs zyzRbdt9sZZ%$!4?Y(mXR*NCy=HN{(Xo~^&!YW+j-rt!SDDgLIeqk)|M105EZ3c<$wZNghg zub`%R?*=z5h9FCCsPPGg)I8<+|Hika8=K$9q{lVRi|Q}{`!Jz$+cNSk`N`(@p{f6f z2vsaE=&ShKzIEc>+TY>)PBy722!j8+Z5j2p!M6vG*X>FTAPUW93sQ;c2^k_4A5d%l z%}T-lGw9Av{Ix!KhamnTE#O!aaK9=34lrcvD*o{vV6PvzzYzb(6=Xv#{y`dGPaL?F z7w;HCGGr_0$O|4|!vJ(#7BB!0Kqh4Ukj7Skf}oAwz>rG-A2Af94jEYjwE{r_hIG&o zLlTpvq=1U+5-T-G@y GGywq24_(9n literal 0 HcmV?d00001 diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/defaults/main.test.bicep b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..a96ed2e5b9 --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,58 @@ +targetScope = 'subscription' + +metadata name = 'Using small parameter set' +metadata description = 'This instance deploys the module with min features enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-virtualmachineimages.azureimagebuilder-${serviceShort}-rg' + +@description('Optional. The location to deploy resource group to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'apvmiaibmin' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +///////////////////////////// +// Template Deployment // +///////////////////////////// +var computeGalleryImageDefinitionName = 'sid-linux' + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + deploymentsToPerform: iteration == 'init' ? 'All' : 'Only base' // Restricting to only infra on re-run as we don't want to back 2 images but only test idempotency + resourceGroupName: resourceGroupName + location: resourceLocation + computeGalleryName: 'gal${namePrefix}${serviceShort}' + computeGalleryImageDefinitionName: computeGalleryImageDefinitionName + assetsStorageAccountName: 'st${namePrefix}${serviceShort}' + computeGalleryImageDefinitions: [ + { + hyperVGeneration: 'V2' + name: 'sid-linux' + osType: 'Linux' + publisher: 'devops' + offer: 'devops_linux' + sku: 'devops_linux_az' + } + ] + imageTemplateImageSource: { + type: 'PlatformImage' + publisher: 'canonical' + offer: 'ubuntu-24_04-lts' + sku: 'server' + version: 'latest' + } + } + } +] diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/main.test.bicep b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/main.test.bicep new file mode 100644 index 0000000000..81918c04b0 --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/main.test.bicep @@ -0,0 +1,136 @@ +targetScope = 'subscription' + +metadata name = 'Deploying all resources' +metadata description = 'This instance deploys the module with the conditions set up to deploy all resource and build the image.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-virtualmachineimages.azureimagebuilder-${serviceShort}-rg' + +@description('Optional. The location to deploy resource group to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'apvmiaiba' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +///////////////////////////// +// Template Deployment // +///////////////////////////// +var computeGalleryImageDefinitionName = 'sid-linux' +var assetsStorageAccountName = 'st${namePrefix}${serviceShort}' +var assetsStorageAccountContainerName = 'aibscripts' +var installPwshScriptName = 'Install-LinuxPowerShell.sh' +var initializeSoftwareScriptName = 'Initialize-LinuxSoftware.ps1' + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + deploymentsToPerform: iteration == 'init' ? 'All' : 'Only base' // Restricting to only infra on re-run as we don't want to back 2 images but only test idempotency + resourceGroupName: resourceGroupName + location: resourceLocation + assetsStorageAccountName: assetsStorageAccountName + assetsStorageAccountContainerName: assetsStorageAccountContainerName + computeGalleryName: 'gal${namePrefix}${serviceShort}' + computeGalleryImageDefinitionName: computeGalleryImageDefinitionName + computeGalleryImageDefinitions: [ + { + hyperVGeneration: 'V2' + name: computeGalleryImageDefinitionName + osType: 'Linux' + publisher: 'devops' + offer: 'devops_linux' + sku: 'devops_linux_az' + } + ] + storageAccountFilesToUpload: [ + { + name: installPwshScriptName + value: loadTextContent('scripts/${installPwshScriptName}') + } + { + name: initializeSoftwareScriptName + value: loadTextContent('scripts/${initializeSoftwareScriptName}') + } + ] + imageTemplateImageSource: { + type: 'PlatformImage' + publisher: 'canonical' + offer: '0001-com-ubuntu-server-jammy' + sku: '22_04-lts-gen2' + version: 'latest' + } + imageTemplateCustomizationSteps: [ + { + type: 'Shell' + name: 'PowerShell installation' + scriptUri: 'https://${assetsStorageAccountName}.blob.${environment().suffixes.storage}/${assetsStorageAccountContainerName}/${installPwshScriptName}' + } + { + type: 'File' + name: 'Download ${initializeSoftwareScriptName}' + sourceUri: 'https://${assetsStorageAccountName}.blob.${environment().suffixes.storage}/${assetsStorageAccountContainerName}/${initializeSoftwareScriptName}' + destination: initializeSoftwareScriptName + } + { + type: 'Shell' + name: 'Software installation' + inline: [ + 'pwsh \'${initializeSoftwareScriptName}\'' + ] + } + ] + + // Windoes example + // var installPwshScriptName = 'Install-WindowsPowerShell.ps1' + // var initializeSoftwareScriptName = 'Initialize-WindowsSoftware.ps1' + // computeGalleryImageDefinitions: [ + // { + // hyperVGeneration: 'V2' + // name: 'sid-windows' + // osType: 'Windows' + // publisher: 'devops' + // offer: 'devops_windows' + // sku: 'devops_windows_az' + // } + // ] + // imageTemplateImageSource: { + // type: 'PlatformImage' + // publisher: 'microsoftwindowsdesktop' + // offer: 'windows-11' + // sku: 'win11-23h2-pro' + // version: 'latest' + // } + // imageTemplateCustomizationSteps: [ + // { + // type: 'PowerShell' + // name: 'PowerShell installation' + // scriptUri: 'https://${assetsStorageAccountName}.blob.${environment().suffixes.storage}/${assetsStorageAccountContainerName}/${installPwshScriptName}' + // runElevated: true + // } + // { + // type: 'File' + // name: 'Download ${initializeSoftwareScriptName}' + // sourceUri: 'https://${assetsStorageAccountName}.blob.${environment().suffixes.storage}/${assetsStorageAccountContainerName}/${initializeSoftwareScriptName}' + // destination: initializeSoftwareScriptName + // } + // { + // type: 'PowerShell' + // name: 'Software installation' + // inline: [ + // 'pwsh \'${initializeSoftwareScriptName}\'' + // ] + // runElevated: true + // } + // ] + } + } +] diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/scripts/Initialize-LinuxSoftware.ps1 b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/scripts/Initialize-LinuxSoftware.ps1 new file mode 100644 index 0000000000..ace80ec04c --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/scripts/Initialize-LinuxSoftware.ps1 @@ -0,0 +1,538 @@ +#region Functions +function LogInfo($message) { + Log 'Info' $message +} +function LogError($message) { + Log 'Error' $message +} +function LogWarning($message) { + Log 'Warning' $message +} + +function Log { + + <# + .SYNOPSIS + Creates a log file and stores logs based on categories with tab seperation + + .PARAMETER category + Category to put into the trace + + .PARAMETER message + Message to be loged + + .EXAMPLE + Log 'Info' 'Message' + + #> + + Param ( + [Parameter(Mandatory = $false)] + [string] $category = 'Info', + + [Parameter(Mandatory = $true)] + [string] $message + ) + + $date = Get-Date + $content = "[$date]`t$category`t`t$message`n" + Write-Verbose $Content -Verbose + + $FilePath = Join-Path ([System.IO.Path]::GetTempPath()) 'log.log' + if (-not (Test-Path $FilePath)) { + Write-Verbose "Log file not found, create new in path: [$FilePath]" -Verbose + $null = New-Item -ItemType 'File' -Path $FilePath -Force + } + Add-Content -Path $FilePath -Value $content -ErrorAction 'Stop' +} + +function Copy-FileAndFolderList { + + param( + [string] $sourcePath, + [string] $targetPath + ) + + $itemsFrom = Get-ChildItem $sourcePath + foreach ($item in $itemsFrom) { + if ($item.PSIsContainer) { + $subsourcePath = $sourcePath + '\' + $item.BaseName + $subtargetPath = $targetPath + '\' + $item.BaseName + $null = Copy-FileAndFolderList -sourcePath $subsourcePath -targetPath $subtargetPath + } else { + $sourceItemPath = $sourcePath + '\' + $item.Name + $targetItemPath = $targetPath + '\' + $item.Name + if (-not (Test-Path $targetItemPath)) { + # only copies non-existing files + if (-not (Test-Path $targetPath)) { + # if folder doesn't exist, creates it + $null = New-Item -ItemType 'directory' -Path $targetPath + } + $null = Copy-Item $sourceItemPath $targetItemPath + } else { + Write-Verbose "[$sourceItemPath] already exists" + } + } + } +} + +function Install-CustomModule { + + <# + .SYNOPSIS + Installes given PowerShell modules + + .DESCRIPTION + Installes given PowerShell modules + + .PARAMETER Module + Required. Modules to be installed, must be Object + @{ + Name = 'Name' + Version = '1.0.0' # Optional + } + + .PARAMETER InstalledModuleList + Optional. Modules that are already installed on the machine. Can be fetched via 'Get-Module -ListAvailable' + + .EXAMPLE + Install-CustomModule @{ Name = 'Pester' } C:\Modules + + Installes pester and saves it to C:\Modules + #> + + [CmdletBinding(SupportsShouldProcess)] + Param ( + [Parameter(Mandatory = $true)] + [Hashtable] $Module, + + [Parameter(Mandatory = $false)] + [object[]] $InstalledModuleList = @() + ) + + # Remove exsisting module in session + if (Get-Module $Module -ErrorAction 'SilentlyContinue') { + try { + Remove-Module $Module -Force + } catch { + LogError('Unable to remove module [{0}] because of exception [{1}]. Stack Trace: [{2}]' -f $Module.Name, $_.Exception, $_.ScriptStackTrace) + } + } + + # Install found module + $moduleImportInputObject = @{ + name = $Module.Name + Repository = 'PSGallery' + } + if ($Module.Version) { + $moduleImportInputObject['RequiredVersion'] = $Module.Version + } + + # Get all modules that match a certain name. In case of e.g. 'Az' it returns several. + $foundModules = Find-Module @moduleImportInputObject + + foreach ($foundModule in $foundModules) { + + # Check if already installed as required + if ($alreadyInstalled = $InstalledModule | Where-Object { $_.Name -eq $Module.Name }) { + if ($Module.Version) { + $alreadyInstalled = $alreadyInstalled | Where-Object { $_.Version -eq $Module.Version } + } else { + # Get latest in case of multiple + $alreadyInstalled = ($alreadyInstalled | Sort-Object -Property Version -Descending)[0] + } + LogInfo('[{0}] Module is already installed with version [{1}]' -f $alreadyInstalled.Name, $alreadyInstalled.Version) -Verbose + continue + } + + # Check if not to be excluded + if ($Module.ExcludeModules -and $Module.excludeModules.contains($foundModule.Name)) { + LogInfo('[{0}] Module is configured to be ignored.' -f $foundModule.Name) -Verbose + continue + } + + if ($PSCmdlet.ShouldProcess('Module [{0}]' -f $foundModule.Name, 'Install')) { + $dependenciesAlreadyAvailable = Get-AreDependenciesAvailable -InstalledModuleList $InstalledModuleList -Module $foundModule + if ($dependenciesAlreadyAvailable) { + LogInfo('[{0}] Install module with version [{1}] exluding dependencies.' -f $foundModule.Name, $foundModule.Version) -Verbose + Install-RawModule -ModuleName $foundModule.Name -ModuleVersion $foundModule.Version + } else { + LogInfo('[{0}] Install module with version [{1}] including dependencies' -f $foundModule.Name, $foundModule.Version) -Verbose + $foundModule | Install-Module -Force -SkipPublisherCheck -AllowClobber + } + } + + if ($installed = (Get-Module -Name $foundModule.Name -ListAvailable | Where-Object { $_.Version -eq $foundModule.Version })) { + + # Adding new module to list of 'already installed' modules + $InstalledModuleList += $installed + + $installPath = Split-Path (Split-Path (Split-Path $installed[0].Path)) + LogInfo('[{0}] Module was installed in path [{2}]' -f $installed[0].Name, $installed[0].Version, $installPath) -Verbose + } else { + LogError('Installation of module [{0}] failed' -f $foundModule.Name) + } + } +} + +function Install-RawModule { + <# + .SYNOPSIS + Install a module without any of its dependencies + + .DESCRIPTION + Modules are downloaded from the PSGallery and stored in the first path of the PSModulePath environment variable + + .PARAMETER ModuleName + Mandatory. The name of the module to install + + .PARAMETER ModuleVersion + Mandatory. The name of the module version to install + + .EXAMPLE + Install-RawModule -ModuleName 'Az.Compute' -ModuleVersion '4.27.0' + + Install module 'Az.Compute' in version '4.27.0' in the default PSModule installation path + #> + + [CmdletBinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory)] + [string] $ModuleName, + + [Parameter(Mandatory)] + [string] $ModuleVersion + ) + + $url = "https://www.powershellgallery.com/api/v2/package/$ModuleName/$ModuleVersion" + + $downloadFolder = Join-Path ([System.IO.Path]::GetTempPath()) 'modulesToInstall' + $downloadPath = Join-Path $downloadFolder "$ModuleName.$ModuleVersion.zip" # Assuming [.zip] instead of [.nupkg] + $expandedRootPath = Join-Path $downloadFolder 'formattedModules' + $expandedPath = Join-Path $expandedRootPath (Split-Path $downloadPath -LeafBase) + $newModuleRootFolder = Join-Path $expandedRootPath $ModuleName + $newModuleRawVersionFolder = (Join-Path $newModuleRootFolder (Split-Path $expandedPath -Leaf)) + + if ($IsWindows) { $psModulesPath = ($env:PSModulePath -split ';')[0] } + else { $psModulesPath = ($env:PSModulePath -split ':')[0] } + + $finalVersionPath = Join-Path $psModulesPath $ModuleName $ModuleVersion + + # 1. Download nupkg package + if (-not (Test-Path $downloadFolder)) { + if ($PSCmdlet.ShouldProcess("Folder [$downloadFolder]", 'Create')) { + $null = New-Item $downloadFolder -ItemType 'Directory' + } + } + try { + if (-not (Test-Path $downloadPath)) { + if ($PSCmdlet.ShouldProcess("From url [$url] to path [$downloadPath]", 'Download')) { + (New-Object System.Net.WebClient).DownloadFile($Url, $downloadPath) + } + } + } catch { + LogError("Download FAILED: $_") + } + + if ($IsWindows) { + # Not supported in Linux + if ($PSCmdlet.ShouldProcess("File in path [$downloadFolder]", 'Unblock')) { + Unblock-File -Path $downloadPath + } + } + + # 2. Expand Achive + if (-not (Test-Path $expandedPath)) { + if ($PSCmdlet.ShouldProcess("File [$downloadPath] to path [$expandedPath]", 'Expand/Unzip')) { + $null = Expand-Archive -Path $downloadPath -DestinationPath $expandedPath -PassThru + } + } + + # 3. Remove files & folders - Optional + foreach ($fileOrFolderToRemove in @('PSGetModuleInfo.xml', '[Content_Types].xml', '_rels', 'package')) { + $filePath = Join-Path $expandedPath $fileOrFolderToRemove + if (Test-Path -LiteralPath $filePath) { + if ($PSCmdlet.ShouldProcess("Item [$filePath]", 'Remove')) { + $null = Remove-Item -LiteralPath $filePath -Force -Recurse -ErrorAction 'SilentlyContinue' + } + } + } + + # 4. Rename folder + $modulename, $moduleVersion = [regex]::Match((Split-Path $downloadPath -LeafBase), '([a-zA-Z.]+)\.([0-9.]+)').Captures.Groups.value[1, 2] + # Rename-Item -Path $expandedPath -NewName + if (-not (Test-Path $newModuleRootFolder)) { + if ($PSCmdlet.ShouldProcess("Folder [$newModuleRootFolder]", 'Create')) { + $null = New-Item -Path $newModuleRootFolder -ItemType 'Directory' + } + if ($PSCmdlet.ShouldProcess("All items from [$expandedPath] to path [$newModuleRootFolder]", 'Move')) { + $null = Move-Item -LiteralPath $expandedPath -Destination $newModuleRootFolder -Force + } + if ($PSCmdlet.ShouldProcess("Folder [$newModuleRawVersionFolder] to name [$ModuleVersion]", 'Rename')) { + $null = Rename-Item -Path (Join-Path $newModuleRootFolder (Split-Path $expandedPath -Leaf)) -NewName $ModuleVersion + } + } + + # 5. Move folder + if (-not (Test-Path $finalVersionPath)) { + if ($PSCmdlet.ShouldProcess("All items from [$newModuleRootFolder] to path [$psModulesPath]", 'Move')) { + $null = Move-Item -LiteralPath $newModuleRootFolder -Destination $psModulesPath -Force + } + } +} + +function Get-AreDependenciesAvailable { + <# + .SYNOPSIS + Check if all depenencies for a given module are already available in the required minimum version. + + .DESCRIPTION + Check if all depenencies for a given module are already available in the required minimum version. + Returns '$true' if they are, otherwise '$false' + + .PARAMETER InstalledModuleList + Optional. A list of already installed modules. + + .PARAMETER Module + Optional. The module to check the dependencies for + + .EXAMPLE + Get-AreDependenciesAvailable -InstalledModuleList (Get-Module -ListAvailable) -Module (Find-Module 'Az.Compute') + + Check if all dependencies of 'Az.Compute' are part of the already installed modules. + #> + + [CmdletBinding()] + param ( + [Parameter(Mandatory = $false)] + [object[]] $InstalledModuleList = @(), + + [Parameter(Mandatory = $false)] + [PSCustomObject] $Module + ) + + foreach ($depenency in $Module.dependencies) { + + $dependencyModuleName = $depenency.Name + $dependencyModuleMinimumVersion = [version] ($depenency.minimumVersion) + + $matchingModulesByName = $InstalledModuleList | Where-Object { $_.Name -eq $dependencyModuleName } + $matchingModules = $matchingModulesByName | Where-Object { ([version] $_.Version) -ge $dependencyModuleMinimumVersion } + + if ($matchingModules.Count -eq 0) { + return $false + } + } + + return $true +} +#endregion + + +$StartTime = Get-Date +$progressPreference = 'SilentlyContinue' +LogInfo('#############################################') +LogInfo('# Entering Initialize-LinuxSoftware.ps1 #') +LogInfo('#############################################') + +########################### +## Install Azure CLI ## +########################### +LogInfo('Install azure cli start') +curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash +LogInfo('Install azure cli end') + +############################### +## Install Extensions CLI # +############################### + +LogInfo('Install cli exentions start') +$Extensions = @( + 'azure-devops' +) +foreach ($extension in $Extensions) { + if ((az extension list-available -o json | ConvertFrom-Json).Name -notcontains $extension) { + Write-Verbose "Adding CLI extension '$extension'" + az extension add --name $extension + } +} +LogInfo('Install cli exentions end') + +########################## +## Install Az Bicep # +########################## +LogInfo('Install az bicep exention start') +az bicep install +LogInfo('Install az bicep exention end') + +######################### +## Install Kubectl # +######################### +LogInfo('Install kubectl start') +sudo az aks install-cli +LogInfo('Install kubectl end') + +######################## +## Install Docker # +######################## +LogInfo('Install docker start') +sudo apt-get update + +sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common +curl -fsSL 'https://download.docker.com/linux/ubuntu/gpg' | sudo apt-key add - + +LogInfo('Install docker - Add repository') +sudo add-apt-repository 'deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable' -y + +LogInfo('Install docker - adp update') +sudo apt-get update + +LogInfo('Install docker - adp-cache docker-ce policy') +apt-cache policy 'docker-ce' + +LogInfo('Install docker - adp update') +sudo apt-get update + +LogInfo('Install docker - adp-get install docker-ce') +sudo DEBIAN_FRONTEND=noninteractive apt-get install -y 'docker-ce' + +LogInfo('Install docker - chmod') +sudo chmod 666 '/var/run/docker.sock' # All users can read and write but cannot execute the file/folder +LogInfo('Install docker end') + +########################### +## Install Terraform ## +########################### +LogInfo('Install Terraform start') +$terraformReleasesUrl = 'https://api.github.com/repos/hashicorp/terraform/releases/latest' +$latestTerraformVersion = (Invoke-WebRequest -Uri $terraformReleasesUrl -UseBasicParsing | ConvertFrom-Json).name.Replace('v', '') +LogInfo("Fetched latest available version: [$latestTerraformVersion]") + +LogInfo("Using version: [$latestTerraformVersion]") +sudo DEBIAN_FRONTEND=noninteractive apt-get install unzip +wget ('https://releases.hashicorp.com/terraform/{0}/terraform_{0}_linux_amd64.zip' -f $latestTerraformVersion) +unzip ('terraform_{0}_linux_amd64.zip' -f $latestTerraformVersion ) +sudo mv terraform /usr/local/bin/ +terraform --version +LogInfo('Install Terraform end') + +####################### +## Install AzCopy # +####################### +# Cleanup +sudo rm ./downloadazcopy-v10-linux* +sudo rm ./azcopy_linux_amd64_* +sudo rm /usr/bin/azcopy + +# Download +wget https://aka.ms/downloadazcopy-v10-linux -O 'downloadazcopy-v10-linux.tar.gz' + +# Expand (to azcopy_linux_amd64_x.x.x) +tar -xzvf downloadazcopy-v10-linux.tar.gz + +# Move +sudo cp ./azcopy_linux_amd64_*/azcopy /usr/bin/ + +################################## +## Install .NET (for Nuget) ## +################################## +# Source: https://docs.microsoft.com/en-us/dotnet/core/install/linux-ubuntu#1804- +LogInfo('Install dotnet (for nuget) start') + +# .NET-Core SDK +sudo apt-get update +sudo DEBIAN_FRONTEND=noninteractive apt-get install -y dotnet-sdk-8.0 + +# .NET-Core Runtime +sudo apt-get update +sudo DEBIAN_FRONTEND=noninteractive apt-get install -y aspnetcore-runtime-8.0 + +LogInfo('Install dotnet (for nuget) end') + +########################### +## Install BICEP CLI ## +########################### +LogInfo('Install BICEP start') + +# Fetch the latest Bicep CLI binary +curl -Lo bicep 'https://github.com/Azure/bicep/releases/latest/download/bicep-linux-x64' +# Mark it as executable +chmod +x ./bicep +# Add bicep to your PATH (requires admin) +sudo mv ./bicep /usr/local/bin/bicep +LogInfo('Install BICEP end') + +############################### +## Install PowerShellGet ## +############################### +LogInfo('Install latest PowerShellGet start') +$null = Install-Module 'PowerShellGet' -Force +LogInfo('Install latest PowerShellGet end') + +LogInfo('Import PowerShellGet start') +$null = Import-PackageProvider PowerShellGet -Force +LogInfo('Import PowerShellGet end') + +#################################### +## Install PowerShell Modules ## +#################################### +$Modules = @( + @{ Name = 'Pester'; Version = '5.1.1' }, + @{ Name = 'PSScriptAnalyzer' }, + @{ Name = 'powershell-yaml' }, + @{ Name = 'Azure.*'; ExcludeModules = @('Azure.Storage') }, # Azure.Storage has AzureRM dependency + @{ Name = 'Logging' }, + @{ Name = 'PoshRSJob' }, + @{ Name = 'ThreadJob' }, + @{ Name = 'JWTDetails' }, + @{ Name = 'OMSIngestionAPI' }, + @{ Name = 'Az.*' }, + @{ Name = 'AzureAD' }, + @{ Name = 'ImportExcel' } +) +$count = 0 +LogInfo('Try installing:') +$modules | ForEach-Object { + LogInfo('- [{0}]. [{1}]' -f $count, $_.Name) + $count++ +} + +# Load already installed modules +$installedModules = Get-Module -ListAvailable + +LogInfo('Install-CustomModule start') +$count = 0 +Foreach ($Module in $Modules) { + LogInfo('=====================') + LogInfo('HANDLING MODULE [{0}] [{1}/{2}]' -f $Module.Name, $count, $Modules.Count) + LogInfo('=====================') + # Installing New Modules and Removing Old + $null = Install-CustomModule -Module $Module -InstalledModuleList $installedModules + $count++ +} +LogInfo('Install-CustomModule end') + + +######################################### +## Post Installation Configuration ## +######################################### +LogInfo('Copy PS modules to expected location start') +$targetPath = '/opt/microsoft/powershell/7/Modules' +$sourcePaths = @('/home/packer/.local/share/powershell/Modules', '/root/.local/share/powershell/Modules') +foreach ($sourcePath in $sourcePaths) { + if (Test-Path $sourcePath) { + LogInfo("Copying from [$sourcePath] to [$targetPath]") + $null = Copy-FileAndFolderList -sourcePath $sourcePath -targetPath $targetPath + } +} +LogInfo('Copy PS modules end') + +$elapsedTime = (Get-Date) - $StartTime +$totalTime = '{0:HH:mm:ss}' -f ([datetime]$elapsedTime.Ticks) +LogInfo("Execution took [$totalTime]") +LogInfo('############################################') +LogInfo('# Exiting Initialize-LinuxSoftware.ps1 #') +LogInfo('############################################') + +return 0 +#endregion diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/scripts/Initialize-WindowsSoftware.ps1 b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/scripts/Initialize-WindowsSoftware.ps1 new file mode 100644 index 0000000000..4672d2b3b6 --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/scripts/Initialize-WindowsSoftware.ps1 @@ -0,0 +1,792 @@ +#requires -Version 6.0 + +#region Functions +function LogInfo($message) { + Log 'Info' $message +} + +function LogError($message) { + Log 'Error' $message +} + +function LogWarning($message) { + Log 'Warning' $message +} + +function Log { + + <# + .SYNOPSIS + Creates a log file and stores logs based on categories with tab seperation + + .PARAMETER category + Category to put into the trace + + .PARAMETER message + Message to be loged + + .EXAMPLE + Log 'Info' 'Message' + + #> + + Param ( + [Parameter(Mandatory = $false)] + [string] $category = 'Info', + + [Parameter(Mandatory = $true)] + [string] $message + ) + + $date = Get-Date + $content = "[$date]`t$category`t`t$message`n" + Write-Verbose $Content -Verbose + + $FilePath = Join-Path ([System.IO.Path]::GetTempPath()) 'log.log' + if (-not (Test-Path $FilePath)) { + Write-Verbose "Log file not found, create new in path: [$FilePath]" -Verbose + $null = New-Item -ItemType 'File' -Path $FilePath -Force + } + Add-Content -Path $FilePath -Value $content -ErrorAction 'Stop' +} + +function Install-CustomModule { + + <# + .SYNOPSIS + Installes given PowerShell modules + + .DESCRIPTION + Installes given PowerShell modules + + .PARAMETER Module + Required. Modules to be installed, must be Object + @{ + Name = 'Name' + Version = '1.0.0' # Optional + } + + .PARAMETER InstalledModuleList + Optional. Modules that are already installed on the machine. Can be fetched via 'Get-Module -ListAvailable' + + .EXAMPLE + Install-CustomModule @{ Name = 'Pester' } C:\Modules + + Installes pester and saves it to C:\Modules + #> + + [CmdletBinding(SupportsShouldProcess)] + Param ( + [Parameter(Mandatory = $true)] + [Hashtable] $Module, + + [Parameter(Mandatory = $false)] + [object[]] $InstalledModuleList = @() + ) + + # Remove exsisting module in session + if (Get-Module $Module -ErrorAction 'SilentlyContinue') { + try { + Remove-Module $Module -Force + } catch { + LogError('Unable to remove module [{0}] because of exception [{1}]. Stack Trace: [{2}]' -f $Module.Name, $_.Exception, $_.ScriptStackTrace) + } + } + + # Install found module + $moduleImportInputObject = @{ + name = $Module.Name + Repository = 'PSGallery' + } + if ($Module.Version) { + $moduleImportInputObject['RequiredVersion'] = $Module.Version + } + + # Get all modules that match a certain name. In case of e.g. 'Az' it returns several. + $foundModules = Find-Module @moduleImportInputObject + + foreach ($foundModule in $foundModules) { + + # Check if already installed as required + if ($alreadyInstalled = $InstalledModule | Where-Object { $_.Name -eq $Module.Name }) { + if ($Module.Version) { + $alreadyInstalled = $alreadyInstalled | Where-Object { $_.Version -eq $Module.Version } + } else { + # Get latest in case of multiple + $alreadyInstalled = ($alreadyInstalled | Sort-Object -Property Version -Descending)[0] + } + LogInfo('[{0}] Module is already installed with version [{1}]' -f $alreadyInstalled.Name, $alreadyInstalled.Version) -Verbose + continue + } + + # Check if not to be excluded + if ($Module.ExcludeModules -and $Module.excludeModules.contains($foundModule.Name)) { + LogInfo('[{0}] Module is configured to be ignored.' -f $foundModule.Name) -Verbose + continue + } + + if ($PSCmdlet.ShouldProcess('Module [{0}]' -f $foundModule.Name, 'Install')) { + $dependenciesAlreadyAvailable = Get-AreDependenciesAvailable -InstalledModuleList $InstalledModuleList -Module $foundModule + if ($dependenciesAlreadyAvailable) { + LogInfo('[{0}] Install module with version [{1}] exluding dependencies.' -f $foundModule.Name, $foundModule.Version) -Verbose + Install-RawModule -ModuleName $foundModule.Name -ModuleVersion $foundModule.Version + } else { + LogInfo('[{0}] Install module with version [{1}] including dependencies' -f $foundModule.Name, $foundModule.Version) -Verbose + $foundModule | Install-Module -Force -SkipPublisherCheck -AllowClobber + } + } + + if ($installed = (Get-Module -Name $foundModule.Name -ListAvailable | Where-Object { $_.Version -eq $foundModule.Version })) { + + # Adding new module to list of 'already installed' modules + $InstalledModuleList += $installed + + $installPath = Split-Path (Split-Path (Split-Path $installed[0].Path)) + LogInfo('[{0}] Module was installed in path [{2}]' -f $installed[0].Name, $installed[0].Version, $installPath) -Verbose + } else { + LogError('Installation of module [{0}] failed' -f $foundModule.Name) + } + } +} + +function Install-RawModule { + <# + .SYNOPSIS + Install a module without any of its dependencies + + .DESCRIPTION + Modules are downloaded from the PSGallery and stored in the first path of the PSModulePath environment variable + + .PARAMETER ModuleName + Mandatory. The name of the module to install + + .PARAMETER ModuleVersion + Mandatory. The name of the module version to install + + .EXAMPLE + Install-RawModule -ModuleName 'Az.Compute' -ModuleVersion '4.27.0' + + Install module 'Az.Compute' in version '4.27.0' in the default PSModule installation path + #> + + [CmdletBinding(SupportsShouldProcess)] + param ( + [Parameter(Mandatory)] + [string] $ModuleName, + + [Parameter(Mandatory)] + [string] $ModuleVersion + ) + + $url = "https://www.powershellgallery.com/api/v2/package/$ModuleName/$ModuleVersion" + + $downloadFolder = Join-Path ([System.IO.Path]::GetTempPath()) 'modulesToInstall' + $downloadPath = Join-Path $downloadFolder "$ModuleName.$ModuleVersion.zip" # Assuming [.zip] instead of [.nupkg] + $expandedRootPath = Join-Path $downloadFolder 'formattedModules' + $expandedPath = Join-Path $expandedRootPath (Split-Path $downloadPath -LeafBase) + $newModuleRootFolder = Join-Path $expandedRootPath $ModuleName + $newModuleRawVersionFolder = (Join-Path $newModuleRootFolder (Split-Path $expandedPath -Leaf)) + + if ($IsWindows) { $psModulesPath = ($env:PSModulePath -split ';')[0] } + else { $psModulesPath = ($env:PSModulePath -split ':')[0] } + + $finalVersionPath = Join-Path $psModulesPath $ModuleName $ModuleVersion + + # 1. Download nupkg package + if (-not (Test-Path $downloadFolder)) { + if ($PSCmdlet.ShouldProcess("Folder [$downloadFolder]", 'Create')) { + $null = New-Item $downloadFolder -ItemType 'Directory' + } + } + try { + if (-not (Test-Path $downloadPath)) { + if ($PSCmdlet.ShouldProcess("From url [$url] to path [$downloadPath]", 'Download')) { + (New-Object System.Net.WebClient).DownloadFile($Url, $downloadPath) + } + } + } catch { + LogError("Download FAILED: $_") + } + + if ($IsWindows) { + # Not supported in Linux + if ($PSCmdlet.ShouldProcess("File in path [$downloadFolder]", 'Unblock')) { + $null = Unblock-File -Path $downloadPath + } + } + + + # 2. Expand Achive + if (-not (Test-Path $expandedPath)) { + if ($PSCmdlet.ShouldProcess("File [$downloadPath] to path [$expandedPath]", 'Expand/Unzip')) { + $null = Expand-Archive -Path $downloadPath -DestinationPath $expandedPath -PassThru + } + } + + # 3. Remove files & folders - Optional + foreach ($fileOrFolderToRemove in @('PSGetModuleInfo.xml', '[Content_Types].xml', '_rels', 'package')) { + $filePath = Join-Path $expandedPath $fileOrFolderToRemove + if (Test-Path -LiteralPath $filePath) { + if ($PSCmdlet.ShouldProcess("Item [$filePath]", 'Remove')) { + $null = Remove-Item -LiteralPath $filePath -Force -Recurse -ErrorAction 'SilentlyContinue' + } + } + } + + # 4. Rename folder + $modulename, $moduleVersion = [regex]::Match((Split-Path $downloadPath -LeafBase), '([a-zA-Z.]+)\.([0-9.]+)').Captures.Groups.value[1, 2] + # Rename-Item -Path $expandedPath -NewName + if (-not (Test-Path $newModuleRootFolder)) { + if ($PSCmdlet.ShouldProcess("Folder [$newModuleRootFolder]", 'Create')) { + $null = New-Item -Path $newModuleRootFolder -ItemType 'Directory' + } + if ($PSCmdlet.ShouldProcess("All items from [$expandedPath] to path [$newModuleRootFolder]", 'Move')) { + $null = Move-Item -LiteralPath $expandedPath -Destination $newModuleRootFolder -Force + } + if ($PSCmdlet.ShouldProcess("Folder [$newModuleRawVersionFolder] to name [$ModuleVersion]", 'Rename')) { + $null = Rename-Item -Path (Join-Path $newModuleRootFolder (Split-Path $expandedPath -Leaf)) -NewName $ModuleVersion + } + } + + # 5. Move folder + if (-not (Test-Path $finalVersionPath)) { + if ($PSCmdlet.ShouldProcess("All items from [$newModuleRootFolder] to path [$psModulesPath]", 'Move')) { + $null = Move-Item -LiteralPath $newModuleRootFolder -Destination $psModulesPath -Force + } + } +} + +function Get-AreDependenciesAvailable { + <# + .SYNOPSIS + Check if all depenencies for a given module are already available in the required minimum version. + + .DESCRIPTION + Check if all depenencies for a given module are already available in the required minimum version. + Returns '$true' if they are, otherwise '$false' + + .PARAMETER InstalledModuleList + Optional. A list of already installed modules. + + .PARAMETER Module + Optional. The module to check the dependencies for + + .EXAMPLE + Get-AreDependenciesAvailable -InstalledModuleList (Get-Module -ListAvailable) -Module (Find-Module 'Az.Compute') + + Check if all dependencies of 'Az.Compute' are part of the already installed modules. + #> + + [CmdletBinding()] + param ( + [Parameter(Mandatory = $false)] + [object[]] $InstalledModuleList = @(), + + [Parameter(Mandatory = $false)] + [PSCustomObject] $Module + ) + + foreach ($depenency in $Module.dependencies) { + + $dependencyModuleName = $depenency.Name + $dependencyModuleMinimumVersion = [version] ($depenency.minimumVersion) + + $matchingModulesByName = $InstalledModuleList | Where-Object { $_.Name -eq $dependencyModuleName } + $matchingModules = $matchingModulesByName | Where-Object { ([version] $_.Version) -ge $dependencyModuleMinimumVersion } + + if ($matchingModules.Count -eq 0) { + return $false + } + } + + return $true +} + +function Set-PowerShellOutputRedirectionBugFix { + + [CmdletBinding(SupportsShouldProcess)] + param () + + $poshMajorVerion = $PSVersionTable.PSVersion.Major + + if ($poshMajorVerion -lt 4) { + try { + # http://www.leeholmes.com/blog/2008/07/30/workaround-the-os-handles-position-is-not-what-filestream-expected/ plus comments + $bindingFlags = [Reflection.BindingFlags] 'Instance,NonPublic,GetField' + $objectRef = $host.GetType().GetField('externalHostRef', $bindingFlags).GetValue($host) + $bindingFlags = [Reflection.BindingFlags] 'Instance,NonPublic,GetProperty' + $consoleHost = $objectRef.GetType().GetProperty('Value', $bindingFlags).GetValue($objectRef, @()) + [void] $consoleHost.GetType().GetProperty('IsStandardOutputRedirected', $bindingFlags).GetValue($consoleHost, @()) + $bindingFlags = [Reflection.BindingFlags] 'Instance,NonPublic,GetField' + $field = $consoleHost.GetType().GetField('standardOutputWriter', $bindingFlags) + + if ($PSCmdlet.ShouldProcess('OutputWriter field [Out]', 'Set')) { + $field.SetValue($consoleHost, [Console]::Out) + } + + [void] $consoleHost.GetType().GetProperty('IsStandardErrorRedirected', $bindingFlags).GetValue($consoleHost, @()) + $field2 = $consoleHost.GetType().GetField('standardErrorWriter', $bindingFlags) + + if ($PSCmdlet.ShouldProcess('OutputWriter field [Error]', 'Set')) { + $field2.SetValue($consoleHost, [Console]::Error) + } + } catch { + LogInfo( 'Unable to apply redirection fix.') + } + } +} + +function Get-Downloader { + param ( + [string]$url + ) + + $downloader = New-Object System.Net.WebClient + + $defaultCreds = [System.Net.CredentialCache]::DefaultCredentials + if ($null -ne $defaultCreds) { + $downloader.Credentials = $defaultCreds + } + + if ($env:chocolateyIgnoreProxy -eq 'true') { + Write-Debug 'Explicitly bypassing proxy due to user environment variable' + $downloader.Proxy = [System.Net.GlobalProxySelection]::GetEmptyWebProxy() + } else { + # check if a proxy is required + $explicitProxy = $env:chocolateyProxyLocation + $explicitProxyUser = $env:chocolateyProxyUser + $explicitProxyPassword = $env:chocolateyProxyPassword + if ($null -ne $explicitProxy -and $explicitProxy -ne '') { + # explicit proxy + $proxy = New-Object System.Net.WebProxy($explicitProxy, $true) + if ($null -ne $explicitProxyPassword -and $explicitProxyPassword -ne '') { + $passwd = ConvertTo-SecureString $explicitProxyPassword -AsPlainText -Force + $proxy.Credentials = New-Object System.Management.Automation.PSCredential ($explicitProxyUser, $passwd) + } + + Write-Debug "Using explicit proxy server '$explicitProxy'." + $downloader.Proxy = $proxy + + } elseif (-not $downloader.Proxy.IsBypassed($url)) { + # system proxy (pass through) + $creds = $defaultCreds + if ($null -eq $creds) { + Write-Debug 'Default credentials were null. Attempting backup method' + $cred = Get-Credential + $creds = $cred.GetNetworkCredential() + } + + $proxyaddress = $downloader.Proxy.GetProxy($url).Authority + Write-Debug "Using system proxy server '$proxyaddress'." + $proxy = New-Object System.Net.WebProxy($proxyaddress) + $proxy.Credentials = $creds + $downloader.Proxy = $proxy + } + } + + return $downloader +} + +function Get-DownloadString { + param ( + [string]$url + ) + $downloader = Get-Downloader $url + + return $downloader.DownloadString($url) +} + +function Get-DownloadedFile { + param ( + [string]$url, + [string]$file + ) + LogInfo( "Downloading $url to $file") + $downloader = Get-Downloader $url + + $downloader.DownloadFile($url, $file) +} + +function Set-SecurityProtocol { + + [CmdletBinding(SupportsShouldProcess)] + param ( + ) + + # Attempt to set highest encryption available for SecurityProtocol. + # PowerShell will not set this by default (until maybe .NET 4.6.x). This + # will typically produce a message for PowerShell v2 (just an info + # message though) + try { + # Set TLS 1.2 (3072), then TLS 1.1 (768), then TLS 1.0 (192), finally SSL 3.0 (48) + # Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't + # exist in .NET 4.0, even though they are addressable if .NET 4.5+ is + # installed (.NET 4.5 is an in-place upgrade). + if ($PSCmdlet.ShouldProcess('Security protocol', 'Set')) { + [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48 + } + } catch { + LogInfo( 'Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to do one or more of the following: (1) upgrade to .NET Framework 4.5+ and PowerShell v3, (2) specify internal Chocolatey package location (set $env:chocolateyDownloadUrl prior to install or host the package internally), (3) use the Download + PowerShell method of install. See https://chocolatey.org/install for all install options.') + } +} + +function Install-Choco { + + LogInfo( 'Install choco') + + LogInfo( 'Invoke install.ps1 content') + $chocTempDir = Join-Path ([System.IO.Path]::GetTempPath()) 'chocolatey' + $tempDir = Join-Path $chocTempDir 'chocInstall' + if (-not [System.IO.Directory]::Exists($tempDir)) { [void][System.IO.Directory]::CreateDirectory($tempDir) } + $file = Join-Path $tempDir 'chocolatey.zip' + + Set-PowerShellOutputRedirectionBugFix + + Set-SecurityProtocol + + LogInfo( 'Getting latest version of the Chocolatey package for download.') + $url = 'https://chocolatey.org/api/v2/Packages()?$filter=((Id%20eq%20%27chocolatey%27)%20and%20(not%20IsPrerelease))%20and%20IsLatestVersion' + [xml]$result = Get-DownloadString $url + $url = $result.feed.entry.content.src + + # Download the Chocolatey package + LogInfo("Getting Chocolatey from $url.") + Get-DownloadedFile $url $file + + # Determine unzipping method + # 7zip is the most compatible so use it by default + $7zaExe = Join-Path $tempDir '7za.exe' + $unzipMethod = '7zip' + if ($env:chocolateyUseWindowsCompression -eq 'true') { + LogInfo( 'Using built-in compression to unzip') + $unzipMethod = 'builtin' + } elseif (-Not (Test-Path ($7zaExe))) { + LogInfo( 'Downloading 7-Zip commandline tool prior to extraction.') + # download 7zip + Get-DownloadedFile 'https://chocolatey.org/7za.exe' "$7zaExe" + } + + # unzip the package + LogInfo("Extracting $file to $tempDir...") + if ($unzipMethod -eq '7zip') { + LogInfo('Unzip with 7zip') + $params = "x -o`"$tempDir`" -bd -y `"$file`"" + # use more robust Process as compared to Start-Process -Wait (which doesn't + # wait for the process to finish in PowerShell v3) + $process = New-Object System.Diagnostics.Process + $process.StartInfo = New-Object System.Diagnostics.ProcessStartInfo($7zaExe, $params) + $process.StartInfo.RedirectStandardOutput = $true + $process.StartInfo.UseShellExecute = $false + $process.StartInfo.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden + $process.Start() | Out-Null + $process.BeginOutputReadLine() + $process.WaitForExit() + $exitCode = $process.ExitCode + $process.Dispose() + $errorMessage = "Unable to unzip package using 7zip. Perhaps try setting `$env:chocolateyUseWindowsCompression = 'true' and call install again. Error:" + switch ($exitCode) { + 0 { LogInfo('Processed zip'); break } + 1 { throw "$errorMessage Some files could not be extracted" } + 2 { throw "$errorMessage 7-Zip encountered a fatal error while extracting the files" } + 7 { throw "$errorMessage 7-Zip command line error" } + 8 { throw "$errorMessage 7-Zip out of memory" } + 255 { throw "$errorMessage Extraction cancelled by the user" } + default { throw "$errorMessage 7-Zip signalled an unknown error (code $exitCode)" } + } + } else { + LogInfo('Unzip without 7zip') + if ($PSVersionTable.PSVersion.Major -lt 5) { + try { + $shellApplication = New-Object -com shell.application + $zipPackage = $shellApplication.NameSpace($file) + $destinationFolder = $shellApplication.NameSpace($tempDir) + $destinationFolder.CopyHere($zipPackage.Items(), 0x10) + } catch { + throw "Unable to unzip package using built-in compression. Set `$env:chocolateyUseWindowsCompression = 'false' and call install again to use 7zip to unzip. Error: `n $_" + } + } else { + $null = Expand-Archive -Path $file -DestinationPath $tempDir -Force -PassThru + } + } + + # Call chocolatey install + LogInfo( 'Installing chocolatey on this machine') + $toolsFolder = Join-Path $tempDir 'tools' + $chocInstallPS1 = Join-Path $toolsFolder 'chocolateyInstall.ps1' + + & $chocInstallPS1 + + LogInfo( 'Ensuring chocolatey commands are on the path') + $chocInstallVariableName = 'ChocolateyInstall' + $chocoPath = [Environment]::GetEnvironmentVariable($chocInstallVariableName) + if ($null -eq $chocoPath -or $chocoPath -eq '') { + $chocoPath = "$env:ALLUSERSPROFILE\Chocolatey" + } + + if (-not (Test-Path ($chocoPath))) { + $chocoPath = "$env:SYSTEMDRIVE\ProgramData\Chocolatey" + } + + $chocoExePath = Join-Path $chocoPath 'bin' + + if ($($env:Path).ToLower().Contains($($chocoExePath).ToLower()) -eq $false) { + $env:Path = [Environment]::GetEnvironmentVariable('Path', [System.EnvironmentVariableTarget]::Machine) + } + + LogInfo( 'Ensuring chocolatey.nupkg is in the lib folder') + $chocoPkgDir = Join-Path $chocoPath 'lib\chocolatey' + $nupkg = Join-Path $chocoPkgDir 'chocolatey.nupkg' + if (-not [System.IO.Directory]::Exists($chocoPkgDir)) { [System.IO.Directory]::CreateDirectory($chocoPkgDir); } + Copy-Item "$file" "$nupkg" -Force -ErrorAction SilentlyContinue +} + + +function Uninstall-AzureRM { + <# + .SYNOPSIS + Removes AzureRM from system + + .EXAMPLE + Uninstall-AzureRM + Removes AzureRM from system + + #> + + LogInfo('Remove Modules from context start') + Get-Module 'AzureRM.*' | Remove-Module + LogInfo('Remaining AzureRM modules: {0}' -f ((Get-Module 'AzureRM.*').Name -join ' | ')) + LogInfo('Remove Modules from context end') + + # Uninstall AzureRm Modules + try { + Get-Module 'AzureRm.*' -ListAvailable | Uninstall-Module -Force + } catch { + LogError("Unable to remove AzureRM Module: $($_.Exception) found, $($_.ScriptStackTrace)") + } + + try { + $AzureRMModuleFolder = 'C:\Program Files (x86)\Microsoft SDKs\Azure\PowerShell\ResourceManager\AzureResourceManager' + if (Test-Path $AzureRMModuleFolder) { + $null = Remove-Item $AzureRMModuleFolder -Force -Recurse + LogInfo("Removed $AzureRMModuleFolder") + } + } catch { + LogError("Unable to remove $AzureRMModuleFolder") + } + + LogInfo('Remaining installed AzureRMModule: {0}' -f ((Get-Module 'AzureRM.*' -ListAvailable).Name -join ' | ')) +} + +function Copy-FileAndFolderList { + + param( + [string] $sourcePath, + [string] $targetPath + ) + + $itemsFrom = Get-ChildItem $sourcePath + foreach ($item in $itemsFrom) { + if ($item.PSIsContainer) { + $subsourcePath = $sourcePath + '\' + $item.BaseName + $subtargetPath = $targetPath + '\' + $item.BaseName + $null = Copy-FileAndFolderList -sourcePath $subsourcePath -targetPath $subtargetPath + } else { + $sourceItemPath = $sourcePath + '\' + $item.Name + $targetItemPath = $targetPath + '\' + $item.Name + if (-not (Test-Path $targetItemPath)) { + # only copies non-existing files + if (-not (Test-Path $targetPath)) { + # if folder doesn't exist, creates it + $null = New-Item -ItemType 'directory' -Path $targetPath + } + $null = Copy-Item $sourceItemPath $targetItemPath + } else { + Write-Verbose "[$sourceItemPath] already exists" + } + } + } +} +#endregion + +$StartTime = Get-Date +$progressPreference = 'SilentlyContinue' +LogInfo('###############################################') +LogInfo('# Entering Initialize-WindowsSoftware.ps1 #') +LogInfo('###############################################') + +LogInfo( 'Set Execution Policy') +Set-ExecutionPolicy Bypass -Scope Process -Force + +####################### +## Install Choco # +####################### +LogInfo('Install-Choco start') +$null = Install-Choco +LogInfo('Install-Choco end') + +########################## +## Install Azure CLI # +########################## +LogInfo('Install azure cli start') +$null = choco install azure-cli -y -v +LogInfo('Install azure cli end') + +############################### +## Install Extensions CLI # +############################### + +LogInfo('Install cli exentions start') +$Extensions = @( + 'azure-devops' +) +foreach ($extension in $Extensions) { + if ((az extension list-available -o json | ConvertFrom-Json).Name -notcontains $extension) { + Write-Verbose "Adding CLI extension '$extension'" + az extension add --name $extension + } +} +LogInfo('Install cli exentions end') + +########################## +## Install Az Bicep # +########################## +LogInfo('Install az bicep exention start') +az bicep install +LogInfo('Install az bicep exention end') + +######################## +## Install docker # +######################## +LogInfo('Install docker start') +choco install docker +LogInfo('Install docker end') + +######################### +## Install Kubectl # +######################### +LogInfo('Install kubectl start') +$null = choco install kubernetes-cli -y -v +LogInfo('Install kubectl end') + +######################## +## Install Docker # +######################## +LogInfo('Install docker start') +$null = choco install docker -y -v +# $null = choco install docker-desktop +LogInfo('Install docker end') + +################################# +## Install PowerShell Core # +################################# +LogInfo('Install powershell core start') +$null = choco install powershell-core -y -v +LogInfo('Install powershell core end') + +########################### +## Install Terraform ## +########################### +LogInfo('Install Terraform start') +$null = choco install terraform -y -v +LogInfo('Install Terraform end') + +####################### +## Install Nuget ## +####################### +LogInfo('Update Package Provider Nuget start') +$null = Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force +LogInfo('Update Package Provider Nuget end') + +####################### +## Install AzCopy # +####################### +LogInfo('Install az copy start') +Invoke-WebRequest -Uri 'https://aka.ms/downloadazcopy-v10-windows' -OutFile 'AzCopy.zip' -UseBasicParsing +$null = Expand-Archive './AzCopy.zip' './AzCopy' -Force -PassThru +Get-ChildItem './AzCopy/*/azcopy.exe' | Move-Item -Destination 'C:\Users\user\AzCopy\AzCopy.exe' +$userenv = [System.Environment]::GetEnvironmentVariable('Path', 'User') +[System.Environment]::SetEnvironmentVariable('PATH', $userenv + ';C:\Users\user\AzCopy', 'User') +LogInfo('Install az copy end') + +############################### +## Install PowerShellGet ## +############################### +LogInfo('Install latest PowerShellGet start') +$null = Install-Module 'PowerShellGet' -Force +LogInfo('Install latest PowerShellGet end') + +LogInfo('Import PowerShellGet start') +$null = Import-PackageProvider PowerShellGet -Force +LogInfo('Import PowerShellGet end') + +#################################### +## Install PowerShell Modules ## +#################################### +$Modules = @( + @{ Name = 'Pester' }, + @{ Name = 'PSScriptAnalyzer' }, + @{ Name = 'powershell-yaml' }, + @{ Name = 'Azure.*'; ExcludeModules = @('Azure.Storage') }, # Azure.Storage has AzureRM dependency + @{ Name = 'Logging' }, + @{ Name = 'PoshRSJob' }, + @{ Name = 'ThreadJob' }, + @{ Name = 'JWTDetails' }, + @{ Name = 'OMSIngestionAPI' }, + @{ Name = 'Az.*' }, + @{ Name = 'AzureAD' }, + @{ Name = 'ImportExcel' } +) +$count = 0 +LogInfo('Try installing:') +$modules | ForEach-Object { + LogInfo('- [{0}]. [{1}]' -f $count, $_.Name) + $count++ +} + +# Load already installed modules +$installedModules = Get-Module -ListAvailable + +LogInfo('Install-CustomModule start') +$count = 0 +Foreach ($Module in $Modules) { + LogInfo('=====================') + LogInfo('HANDLING MODULE [{0}] [{1}/{2}]' -f $Module.Name, $count, $Modules.Count) + LogInfo('=====================') + # Installing New Modules and Removing Old + $null = Install-CustomModule -Module $Module -InstalledModuleList $installedModules + $count++ +} +LogInfo('Install-CustomModule end') + +######################################### +## Post Installation Configuration ## +######################################### +LogInfo('Copy PS modules to expected location start') +$targetPath = 'C:\program files\powershell\7\Modules' +$sourcePaths = @('C:\Users\packer\Documents\PowerShell\Modules') +foreach ($sourcePath in $sourcePaths) { + if (Test-Path $sourcePath) { + LogInfo("Copying from [$sourcePath] to [$targetPath]") + $null = Copy-FileAndFolderList -sourcePath $sourcePath -targetPath $targetPath + } +} +LogInfo('Copy PS modules end') + +######################################### +## Post Installation Configuration ## +######################################### +if (Get-Module AzureRm* -ListAvailable) { + LogInfo('Un-install ARM start') + Uninstall-AzureRm + LogInfo('Un-install ARM end') +} + +$elapsedTime = (Get-Date) - $StartTime +$totalTime = '{0:HH:mm:ss}' -f ([datetime]$elapsedTime.Ticks) +LogInfo("Execution took [$totalTime]") +LogInfo('##############################################') +LogInfo('# Exiting Initialize-WindowsSoftware.ps1 #') +LogInfo('##############################################') + +return 0 +#endregion diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/scripts/Install-LinuxPowerShell.sh b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/scripts/Install-LinuxPowerShell.sh new file mode 100644 index 0000000000..e62a245487 --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/scripts/Install-LinuxPowerShell.sh @@ -0,0 +1,44 @@ +# Source: https://learn.microsoft.com/en-us/powershell/scripting/install/install-ubuntu?view=powershell-7.4 + +echo '###########################################' +echo '# Entering Install-LinuxPowerShell.sh #' +echo '###########################################' + +echo '1. Update the list of packages' +sudo apt-get update + +echo '2. Install pre-requisite packages' +sudo apt-get install -y wget apt-transport-https software-properties-common + +echo '3. Get the version of Ubuntu' +# source /etc/os-release +# echo "Found version $VERSION_ID" - empty +VERSION_ID='22.04' + +echo '4. Determine URL' +url=https://packages.microsoft.com/config/ubuntu/$VERSION_ID/packages-microsoft-prod.deb +echo " Found URL [$url]" + +echo '5. Download the Microsoft repository GPG keys' +wget -q $url + +echo '6. Register the Microsoft repository GPG keys' +sudo dpkg -i packages-microsoft-prod.deb + +echo '7. Delete the Microsoft repository keys file' +rm packages-microsoft-prod.deb + +echo '8. Update the list of products' +sudo apt-get update + +echo '9. Enable the "universe" repositories' +sudo add-apt-repository universe -y + +echo '10. Install PowerShell' +sudo apt-get install -y powershell + +echo '##########################################' +echo '# Exiting Install-LinuxPowerShell.sh #' +echo '##########################################' + +exit 0 diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/scripts/Install-WindowsPowerShell.ps1 b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/scripts/Install-WindowsPowerShell.ps1 new file mode 100644 index 0000000000..2ecdce36eb --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployAll/scripts/Install-WindowsPowerShell.ps1 @@ -0,0 +1,43 @@ +Write-Verbose '##############################################' -Verbose +Write-Verbose '# Entering Install-WindowsPowerShell.ps1 #' -Verbose +Write-Verbose '##############################################' -Verbose + +$psVersion = '7.4.4' +$ps7Url = "https://github.com/PowerShell/PowerShell/releases/download/v$psVersion/PowerShell-$psVersion-win-x64.zip" +$downloadFolder = (Get-Location).Path +$downloadLoc = Join-Path $downloadFolder (Split-Path $ps7Url -Leaf) + +if (-not (Test-Path $downloadLoc)) { + Write-Verbose "Download to [$downloadLoc]" -Verbose + (New-Object System.Net.WebClient).DownloadFile($ps7Url, $downloadLoc) + Unblock-File $downloadLoc + Write-Verbose 'Downloaded' -Verbose +} else { + Write-Verbose "Already downloaded to [$downloadLoc]" -Verbose +} + +$installLoc = "$env:ProgramFiles\PowerShell\7" + +if (-not (Test-Path $installLoc)) { + Write-Verbose "Install to [$installLoc]" -Verbose + Expand-Archive -Path $downloadLoc -DestinationPath $installLoc + Write-Verbose 'Installed' -Verbose +} else { + Write-Verbose "Already installed in [$installLoc]" -Verbose +} + +if ($Env:PATH -notlike "*$installLoc*") { + Write-Verbose 'Set environment variable' -Verbose + [Environment]::SetEnvironmentVariable('PATH', $Env:PATH + ";$installLoc", [EnvironmentVariableTarget]::Machine) + $env:Path += ";$installLoc" + Write-Verbose 'Environment variable set' -Verbose +} else { + Write-Verbose 'Environment variable already set' -Verbose +} + +Write-Verbose 'Try run PS-Core' -Verbose +pwsh -Command 'Write-Host "Hello from the inside"' + +Write-Verbose '#############################################' -Verbose +Write-Verbose '# Exiting Install-WindowsPowerShell.ps1 #' -Verbose +Write-Verbose '#############################################' -Verbose diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyAssetsAndImage/dependencies.bicep b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyAssetsAndImage/dependencies.bicep new file mode 100644 index 0000000000..ca5c99c459 --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyAssetsAndImage/dependencies.bicep @@ -0,0 +1,259 @@ +targetScope = 'subscription' + +@description('Required. The name of the Resource Group.') +param resourceGroupName string + +@description('Required. The name of the Resource Group to deploy the Image Template resources into.') +param imageTemplateResourceGroupName string + +// User Assigned Identity (MSI) Parameters +@description('Required. The name of the Managed Identity used by deployment scripts.') +param deploymentScriptManagedIdentityName string + +@description('Required. The name of the Managed Identity used by the Azure Image Builder.') +param imageManagedIdentityName string + +// Azure Compute Gallery Parameters +@description('Required. The name of the Azure Compute Gallery.') +param computeGalleryName string + +// Storage Account Parameters +@description('Required. The name of the storage account. Only needed if you want to upload scripts to be used during image baking.') +param assetsStorageAccountName string + +@description('Required. The name of the storage account.') +param deploymentScriptStorageAccountName string + +// Virtual Network Parameters +@description('Required. The name of the Virtual Network.') +param virtualNetworkName string + +// Shared Parameters +@description('Optional. The location to deploy into.') +param location string = deployment().location + +var addressPrefix = '10.0.0.0/16' + +// The Image Definitions in the Azure Compute Gallery +var computeGalleryImageDefinitionsVar = [ + { + hyperVGeneration: 'V2' + name: 'sid-linux' + osType: 'Linux' + publisher: 'devops' + offer: 'devops_linux' + sku: 'devops_linux_az' + } +] +var assetsStorageAccountContainerName = 'aibscripts' + +// Role required for deployment script to be able to use a storage account via private networking +resource storageFileDataPrivilegedContributorRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: '69566ab7-960f-475b-8e7c-b3118f30c6bd' // Storage File Data Priveleged Contributor + scope: tenant() +} +resource contributorRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: 'b24988ac-6180-42a0-ab88-20f7382dd24c' // Contributor + scope: tenant() +} + +// Resource Groups +resource rg 'Microsoft.Resources/resourceGroups@2024-03-01' = { + name: resourceGroupName + location: location +} + +// Always deployed as both an infra element & needed as a staging resource group for image building +module imageTemplateRg 'br/public:avm/res/resources/resource-group:0.2.4' = { + name: '${deployment().name}-image-rg' + params: { + name: imageTemplateResourceGroupName + location: location + } +} + +// User Assigned Identity (MSI) +module dsMsi 'br/public:avm/res/managed-identity/user-assigned-identity:0.2.2' = { + name: '${deployment().name}-ds-msi' + scope: rg + params: { + name: deploymentScriptManagedIdentityName + location: location + } +} + +module imageMSI 'br/public:avm/res/managed-identity/user-assigned-identity:0.2.2' = { + name: '${deployment().name}-image-msi' + scope: rg + params: { + name: imageManagedIdentityName + location: location + } +} + +// MSI Subscription contributor assignment +resource imageMSI_rbac 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().subscriptionId, imageManagedIdentityName, contributorRole.id) + properties: { + principalId: imageMSI.outputs.principalId + roleDefinitionId: contributorRole.id + principalType: 'ServicePrincipal' + } +} + +// Azure Compute Gallery +module azureComputeGallery 'br/public:avm/res/compute/gallery:0.4.0' = { + name: '${deployment().name}-acg' + scope: rg + params: { + name: computeGalleryName + images: computeGalleryImageDefinitionsVar + location: location + } +} + +// Image Template Virtual Network +module vnet 'br/public:avm/res/network/virtual-network:0.1.6' = { + name: '${deployment().name}-vnet' + scope: rg + params: { + name: virtualNetworkName + addressPrefixes: [ + addressPrefix + ] + subnets: [ + { + name: 'subnet-it' + addressPrefix: cidrSubnet(addressPrefix, 24, 0) + privateLinkServiceNetworkPolicies: 'Disabled' // Required if using Azure Image Builder with existing VNET + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + } + ] + } + { + name: 'subnet-ds' + addressPrefix: cidrSubnet(addressPrefix, 24, 1) + privateLinkServiceNetworkPolicies: 'Disabled' // Required if using Azure Image Builder with existing VNET - temp + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + } + ] + delegations: [ + { + name: 'Microsoft.ContainerInstance.containerGroups' + properties: { + serviceName: 'Microsoft.ContainerInstance/containerGroups' + } + } + ] + } + ] + location: location + } +} + +// Assets Storage Account +module assetsStorageAccount 'br/public:avm/res/storage/storage-account:0.9.1' = { + name: '${deployment().name}-files-sa' + scope: rg + params: { + name: assetsStorageAccountName + allowSharedKeyAccess: false // Keys not needed if MSI is granted access + location: location + networkAcls: { + defaultAction: 'Allow' + } + blobServices: { + containers: [ + { + name: assetsStorageAccountContainerName + publicAccess: 'None' + roleAssignments: [ + { + // Allow Infra MSI to access storage account container to upload files - DO NOT REMOVE + roleDefinitionIdOrName: 'Storage Blob Data Contributor' + principalId: dsMsi.outputs.principalId + principalType: 'ServicePrincipal' + } + { + // Allow image MSI to access storage account container to read files - DO NOT REMOVE + roleDefinitionIdOrName: 'Storage Blob Data Reader' + principalId: imageMSI.outputs.principalId + principalType: 'ServicePrincipal' + } + ] + } + ] + } + } +} + +// Deployment scripts & their storage account +module dsStorageAccount 'br/public:avm/res/storage/storage-account:0.9.1' = { + name: '${deployment().name}-ds-sa' + scope: rg + params: { + name: deploymentScriptStorageAccountName + allowSharedKeyAccess: true // May not be disabled to allow deployment script to access storage account files + roleAssignments: [ + { + // Allow MSI to leverage the storage account for private networking of container instance + // ref: https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/deployment-script-bicep#access-private-virtual-network + roleDefinitionIdOrName: storageFileDataPrivilegedContributorRole.id // Storage File Data Priveleged Contributor + principalId: dsMsi.outputs.principalId + principalType: 'ServicePrincipal' + } + ] + location: location + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + virtualNetworkRules: [ + { + // Allow deployment script to use storage account for private networking of container instance + action: 'Allow' + id: vnet.outputs.subnetResourceIds[1] // subnet-ds + } + ] + } + } +} + +@description('The image definitions used in the Azure Compute Gallery.') +output computeGalleryImageDefinitions array = computeGalleryImageDefinitionsVar + +@description('The name of the created Resource Group.') +output resourceGroupName string = rg.name + +@description('The name of the created Azure Compute Gallery') +output computeGalleryName string = azureComputeGallery.outputs.name + +@description('The name of the created Virtual Network') +output virtualNetworkName string = vnet.outputs.name + +@description('The name of the Storage Account Container hosting the customization files used by the Azure Image Builder.') +output assetsStorageAccountContainerName string = assetsStorageAccountContainerName + +@description('The name of the create Storage Account hosting the customization files used by the Azure Image Builder.') +output assetsStorageAccountName string = assetsStorageAccount.outputs.name + +@description('The name of the User-Assigned-Identity used by the Deployment Scripts.') +output deploymentScriptManagedIdentityName string = dsMsi.outputs.name + +@description('The name of the Storage Account used by the Deployment Scripts.') +output deploymentScriptStorageAccountName string = dsStorageAccount.outputs.name + +@description('The name of the subnet used by the Azure Image Builder.') +output imageSubnetName string = last(split(vnet.outputs.subnetResourceIds[0], '/')) + +@description('The name of the subnet used by the Deployment Scripts.') +output deploymentScriptSubnetName string = last(split(vnet.outputs.subnetResourceIds[1], '/')) + +@description('The name of the User-Assigned-Identity used by the Azure Image Builder.') +output imageManagedIdentityName string = imageMSI.outputs.name + +@description('The name of the Resource Group used by the Azure Image Builder.') +output imageTemplateResourceGroupName string = imageTemplateRg.outputs.name diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyAssetsAndImage/main.test.bicep b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyAssetsAndImage/main.test.bicep new file mode 100644 index 0000000000..849d005a0e --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyAssetsAndImage/main.test.bicep @@ -0,0 +1,90 @@ +targetScope = 'subscription' + +metadata name = 'Deploying only the assets & image' +metadata description = 'This instance deploys the module with the conditions set up to only update the assets on the assets storage account and build the image, assuming all dependencies are setup.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-virtualmachineimages.azureimagebuilder-${serviceShort}-rg' + +@description('Optional. The location to deploy resource group to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'apvmiaiboaai' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module nestedDependencies 'dependencies.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + // managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + computeGalleryName: 'dep${namePrefix}gal${serviceShort}' + deploymentScriptManagedIdentityName: 'dep-${namePrefix}-ds-msi-${serviceShort}' + imageManagedIdentityName: 'dep-${namePrefix}-it-msi-${serviceShort}' + resourceGroupName: resourceGroupName + imageTemplateResourceGroupName: '${resourceGroupName}-image-build' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + deploymentScriptStorageAccountName: 'dep${namePrefix}dsst${serviceShort}' + assetsStorageAccountName: 'dep${namePrefix}ast${serviceShort}' + location: resourceLocation + } +} + +///////////////////////////// +// Template Deployment // +///////////////////////////// +var exampleScriptName = 'exampleScript.sh' + +// No idempotency test as we don't want to bake 2 images +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + deploymentsToPerform: 'Only assets & image' + resourceGroupName: nestedDependencies.outputs.resourceGroupName + location: resourceLocation + computeGalleryName: nestedDependencies.outputs.computeGalleryName + computeGalleryImageDefinitionName: nestedDependencies.outputs.computeGalleryImageDefinitions[0].name + computeGalleryImageDefinitions: nestedDependencies.outputs.computeGalleryImageDefinitions + virtualNetworkName: nestedDependencies.outputs.virtualNetworkName + assetsStorageAccountContainerName: nestedDependencies.outputs.assetsStorageAccountContainerName + assetsStorageAccountName: nestedDependencies.outputs.assetsStorageAccountName + deploymentScriptManagedIdentityName: nestedDependencies.outputs.deploymentScriptManagedIdentityName + deploymentScriptStorageAccountName: nestedDependencies.outputs.deploymentScriptStorageAccountName + deploymentScriptSubnetName: nestedDependencies.outputs.deploymentScriptSubnetName + imageManagedIdentityName: nestedDependencies.outputs.imageManagedIdentityName + imageSubnetName: nestedDependencies.outputs.imageSubnetName + imageTemplateResourceGroupName: nestedDependencies.outputs.imageTemplateResourceGroupName + imageTemplateCustomizationSteps: [ + { + type: 'Shell' + name: 'Example script' + scriptUri: 'https://${nestedDependencies.outputs.assetsStorageAccountName}.blob.${az.environment().suffixes.storage}/${nestedDependencies.outputs.assetsStorageAccountContainerName}/${exampleScriptName}' + } + ] + imageTemplateImageSource: { + type: 'PlatformImage' + publisher: 'canonical' + offer: 'ubuntu-24_04-lts' + sku: 'server' + version: 'latest' + } + storageAccountFilesToUpload: [ + { + name: exampleScriptName + value: loadTextContent('scripts/${exampleScriptName}') + } + ] + } +} diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyAssetsAndImage/scripts/exampleScript.sh b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyAssetsAndImage/scripts/exampleScript.sh new file mode 100644 index 0000000000..22cd6c612f --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyAssetsAndImage/scripts/exampleScript.sh @@ -0,0 +1,4 @@ +echo '###############################################' +echo '# Hey there. Just checking the place out. #' +echo '###############################################' + diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyBase/main.test.bicep b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyBase/main.test.bicep new file mode 100644 index 0000000000..1735445bde --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyBase/main.test.bicep @@ -0,0 +1,59 @@ +targetScope = 'subscription' + +metadata name = 'Deploying only the base services' +metadata description = 'This instance deploys the module with the conditions set up to only deploy the base resources, that is everything but the image.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-virtualmachineimages.azureimagebuilder-${serviceShort}-rg' + +@description('Optional. The location to deploy resource group to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'apvmiaibob' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +///////////////////////////// +// Template Deployment // +///////////////////////////// +var computeGalleryImageDefinitionName = 'sid-linux' + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + deploymentsToPerform: 'Only base' + resourceGroupName: resourceGroupName + location: resourceLocation + assetsStorageAccountName: 'st${namePrefix}${serviceShort}' + imageManagedIdentityName: 'msi-it-${namePrefix}-${serviceShort}' + computeGalleryName: 'gal${namePrefix}${serviceShort}' + computeGalleryImageDefinitionName: computeGalleryImageDefinitionName + computeGalleryImageDefinitions: [ + { + hyperVGeneration: 'V2' + name: computeGalleryImageDefinitionName + osType: 'Linux' + publisher: 'devops' + offer: 'devops_linux' + sku: 'devops_linux_az' + } + ] + imageTemplateImageSource: { + type: 'PlatformImage' + publisher: 'canonical' + offer: 'ubuntu-24_04-lts' + sku: 'server' + version: 'latest' + } + } + } +] diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyImage/dependencies.bicep b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyImage/dependencies.bicep new file mode 100644 index 0000000000..fe793a0e85 --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyImage/dependencies.bicep @@ -0,0 +1,303 @@ +targetScope = 'subscription' + +@description('Required. The name of the Resource Group.') +param resourceGroupName string + +@description('Required. The name of the Resource Group to deploy the Image Template resources into.') +param imageTemplateResourceGroupName string + +// User Assigned Identity (MSI) Parameters +@description('Required. The name of the Managed Identity used by deployment scripts.') +param deploymentScriptManagedIdentityName string + +@description('Required. The name of the Managed Identity used by the Azure Image Builder.') +param imageManagedIdentityName string + +// Azure Compute Gallery Parameters +@description('Required. The name of the Azure Compute Gallery.') +param computeGalleryName string + +// Storage Account Parameters +@description('Required. The name of the storage account. Only needed if you want to upload scripts to be used during image baking.') +param assetsStorageAccountName string + +@description('Required. The name of the storage account.') +param deploymentScriptStorageAccountName string + +@description('Required. The name of the Deployment Script to the Storage Upload.') +param storageDeploymentScriptName string + +// Virtual Network Parameters +@description('Required. The name of the Virtual Network.') +param virtualNetworkName string + +// Shared Parameters +@description('Optional. The location to deploy into.') +param location string = deployment().location + +var exampleScriptName = 'exampleScript.sh' +var addressPrefix = '10.0.0.0/16' + +// The Image Definitions in the Azure Compute Gallery +var computeGalleryImageDefinitionsVar = [ + { + hyperVGeneration: 'V2' + name: 'sid-linux' + osType: 'Linux' + publisher: 'devops' + offer: 'devops_linux' + sku: 'devops_linux_az' + } +] +var assetsStorageAccountContainerName = 'aibscripts' + +// Role required for deployment script to be able to use a storage account via private networking +resource storageFileDataPrivilegedContributorRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: '69566ab7-960f-475b-8e7c-b3118f30c6bd' // Storage File Data Priveleged Contributor + scope: tenant() +} +resource contributorRole 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = { + name: 'b24988ac-6180-42a0-ab88-20f7382dd24c' // Contributor + scope: tenant() +} + +// Resource Groups +resource rg 'Microsoft.Resources/resourceGroups@2024-03-01' = { + name: resourceGroupName + location: location +} + +// Always deployed as both an infra element & needed as a staging resource group for image building +module imageTemplateRg 'br/public:avm/res/resources/resource-group:0.2.4' = { + name: '${deployment().name}-image-rg' + params: { + name: imageTemplateResourceGroupName + location: location + } +} + +// User Assigned Identity (MSI) +module dsMsi 'br/public:avm/res/managed-identity/user-assigned-identity:0.2.2' = { + name: '${deployment().name}-ds-msi' + scope: rg + params: { + name: deploymentScriptManagedIdentityName + location: location + } +} + +module imageMSI 'br/public:avm/res/managed-identity/user-assigned-identity:0.2.2' = { + name: '${deployment().name}-image-msi' + scope: rg + params: { + name: imageManagedIdentityName + location: location + } +} + +// MSI Subscription contributor assignment +resource imageMSI_rbac 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().subscriptionId, imageManagedIdentityName, contributorRole.id) + properties: { + principalId: imageMSI.outputs.principalId + roleDefinitionId: contributorRole.id + principalType: 'ServicePrincipal' + } +} + +// Azure Compute Gallery +module azureComputeGallery 'br/public:avm/res/compute/gallery:0.4.0' = { + name: '${deployment().name}-acg' + scope: rg + params: { + name: computeGalleryName + images: computeGalleryImageDefinitionsVar + location: location + } +} + +// Image Template Virtual Network +module vnet 'br/public:avm/res/network/virtual-network:0.1.6' = { + name: '${deployment().name}-vnet' + scope: rg + params: { + name: virtualNetworkName + addressPrefixes: [ + addressPrefix + ] + subnets: [ + { + name: 'subnet-it' + addressPrefix: cidrSubnet(addressPrefix, 24, 0) + privateLinkServiceNetworkPolicies: 'Disabled' // Required if using Azure Image Builder with existing VNET + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + } + ] + } + { + name: 'subnet-ds' + addressPrefix: cidrSubnet(addressPrefix, 24, 1) + privateLinkServiceNetworkPolicies: 'Disabled' // Required if using Azure Image Builder with existing VNET - temp + serviceEndpoints: [ + { + service: 'Microsoft.Storage' + } + ] + delegations: [ + { + name: 'Microsoft.ContainerInstance.containerGroups' + properties: { + serviceName: 'Microsoft.ContainerInstance/containerGroups' + } + } + ] + } + ] + location: location + } +} + +// Assets Storage Account +module assetsStorageAccount 'br/public:avm/res/storage/storage-account:0.9.1' = { + name: '${deployment().name}-files-sa' + scope: rg + params: { + name: assetsStorageAccountName + allowSharedKeyAccess: false // Keys not needed if MSI is granted access + location: location + networkAcls: { + defaultAction: 'Allow' + } + blobServices: { + containers: [ + { + name: assetsStorageAccountContainerName + publicAccess: 'None' + roleAssignments: [ + { + // Allow Infra MSI to access storage account container to upload files - DO NOT REMOVE + roleDefinitionIdOrName: 'Storage Blob Data Contributor' + principalId: dsMsi.outputs.principalId + principalType: 'ServicePrincipal' + } + { + // Allow image MSI to access storage account container to read files - DO NOT REMOVE + roleDefinitionIdOrName: 'Storage Blob Data Reader' + principalId: imageMSI.outputs.principalId + principalType: 'ServicePrincipal' + } + ] + } + ] + } + } +} + +// Deployment scripts & their storage account +module dsStorageAccount 'br/public:avm/res/storage/storage-account:0.9.1' = { + name: '${deployment().name}-ds-sa' + scope: rg + params: { + name: deploymentScriptStorageAccountName + allowSharedKeyAccess: true // May not be disabled to allow deployment script to access storage account files + roleAssignments: [ + { + // Allow MSI to leverage the storage account for private networking of container instance + // ref: https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/deployment-script-bicep#access-private-virtual-network + roleDefinitionIdOrName: storageFileDataPrivilegedContributorRole.id // Storage File Data Priveleged Contributor + principalId: dsMsi.outputs.principalId + principalType: 'ServicePrincipal' + } + ] + location: location + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + virtualNetworkRules: [ + { + // Allow deployment script to use storage account for private networking of container instance + action: 'Allow' + id: vnet.outputs.subnetResourceIds[1] // subnet-ds + } + ] + } + } +} + +// Upload storage account files +module storageAccount_upload 'br/public:avm/res/resources/deployment-script:0.3.1' = { + name: '${deployment().name}-storage-upload-ds' + scope: resourceGroup(resourceGroupName) + params: { + name: storageDeploymentScriptName + kind: 'AzurePowerShell' + azPowerShellVersion: '12.0' + managedIdentities: { + userAssignedResourcesIds: [ + resourceId( + subscription().subscriptionId, + resourceGroupName, + 'Microsoft.ManagedIdentity/userAssignedIdentities', + deploymentScriptManagedIdentityName + ) + ] + } + scriptContent: loadTextContent('../../../../../../utilities/e2e-template-assets/scripts/Set-StorageContainerContentByEnvVar.ps1') + environmentVariables: [ + { + name: '__SCRIPT__${replace(replace(exampleScriptName, '-', '__'), '.', '_') }' // May only be alphanumeric characters & underscores. The upload will replace '_' with '.' and '__' with '-'. E.g., Install__LinuxPowerShell_sh will be Install-LinuxPowerShell.sh + value: loadTextContent('scripts/${exampleScriptName}') + } + ] + + arguments: ' -StorageAccountName "${assetsStorageAccountName}" -TargetContainer "${assetsStorageAccountContainerName}"' + timeout: 'PT30M' + cleanupPreference: 'Always' + location: location + storageAccountResourceId: dsStorageAccount.outputs.resourceId + subnetResourceIds: [ + vnet.outputs.subnetResourceIds[1] // subnet-ds + ] + } +} + +@description('The image definitions used in the Azure Compute Gallery.') +output computeGalleryImageDefinitions array = computeGalleryImageDefinitionsVar + +@description('The name of the created Resource Group.') +output resourceGroupName string = rg.name + +@description('The name of the created Azure Compute Gallery') +output computeGalleryName string = azureComputeGallery.outputs.name + +@description('The name of the created Virtual Network') +output virtualNetworkName string = vnet.outputs.name + +@description('The name of the Storage Account Container hosting the customization files used by the Azure Image Builder.') +output assetsStorageAccountContainerName string = assetsStorageAccountContainerName + +@description('The name of the create Storage Account hosting the customization files used by the Azure Image Builder.') +output assetsStorageAccountName string = assetsStorageAccount.outputs.name + +@description('The name of the User-Assigned-Identity used by the Deployment Scripts.') +output deploymentScriptManagedIdentityName string = dsMsi.outputs.name + +@description('The name of the Storage Account used by the Deployment Scripts.') +output deploymentScriptStorageAccountName string = dsStorageAccount.outputs.name + +@description('The name of the subnet used by the Azure Image Builder.') +output imageSubnetName string = last(split(vnet.outputs.subnetResourceIds[0], '/')) + +@description('The name of the subnet used by the Deployment Scripts.') +output deploymentScriptSubnetName string = last(split(vnet.outputs.subnetResourceIds[1], '/')) + +@description('The name of the User-Assigned-Identity used by the Azure Image Builder.') +output imageManagedIdentityName string = imageMSI.outputs.name + +@description('The name of the Resource Group used by the Azure Image Builder.') +output imageTemplateResourceGroupName string = imageTemplateRg.outputs.name + +@description('The name of the script uploaded to the Assets Storage Account to use in the Azure Image Builder customization steps.') +output exampleScriptName string = exampleScriptName diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyImage/main.test.bicep b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyImage/main.test.bicep new file mode 100644 index 0000000000..c777fa31da --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyImage/main.test.bicep @@ -0,0 +1,87 @@ +targetScope = 'subscription' + +metadata name = 'Deploying only the image' +metadata description = 'This instance deploys the module with the conditions set up to only deploy and bake the image, assuming all dependencies are setup.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-virtualmachineimages.azureimagebuilder-${serviceShort}-rg' + +@description('Optional. The location to deploy resource group to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'apvmiaiboi' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +@description('Generated. Do not provide a value! This date value is used to generate a SAS token to access the modules.') +param baseTime string = utcNow() + +var formattedTime = replace(replace(replace(baseTime, ':', ''), '-', ''), ' ', '') + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +module nestedDependencies 'dependencies.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + // managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + computeGalleryName: 'dep${namePrefix}gal${serviceShort}' + deploymentScriptManagedIdentityName: 'dep-${namePrefix}-ds-msi-${serviceShort}' + imageManagedIdentityName: 'dep-${namePrefix}-it-msi-${serviceShort}' + resourceGroupName: resourceGroupName + imageTemplateResourceGroupName: '${resourceGroupName}-image-build' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + deploymentScriptStorageAccountName: 'dep${namePrefix}dsst${serviceShort}' + storageDeploymentScriptName: 'dep-${namePrefix}-ds-${serviceShort}-${formattedTime}' + assetsStorageAccountName: 'dep${namePrefix}ast${serviceShort}' + location: resourceLocation + } +} + +///////////////////////////// +// Template Deployment // +///////////////////////////// + +// No idempotency test as we don't want to bake 2 images +module testDeployment '../../../main.bicep' = { + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + deploymentsToPerform: 'Only image' + resourceGroupName: nestedDependencies.outputs.resourceGroupName + location: resourceLocation + computeGalleryName: nestedDependencies.outputs.computeGalleryName + computeGalleryImageDefinitions: nestedDependencies.outputs.computeGalleryImageDefinitions + computeGalleryImageDefinitionName: nestedDependencies.outputs.computeGalleryImageDefinitions[0].name + virtualNetworkName: nestedDependencies.outputs.virtualNetworkName + deploymentScriptManagedIdentityName: nestedDependencies.outputs.deploymentScriptManagedIdentityName + deploymentScriptStorageAccountName: nestedDependencies.outputs.deploymentScriptStorageAccountName + deploymentScriptSubnetName: nestedDependencies.outputs.deploymentScriptSubnetName + imageManagedIdentityName: nestedDependencies.outputs.imageManagedIdentityName + imageSubnetName: nestedDependencies.outputs.imageSubnetName + imageTemplateResourceGroupName: nestedDependencies.outputs.imageTemplateResourceGroupName + imageTemplateImageSource: { + type: 'PlatformImage' + publisher: 'canonical' + offer: 'ubuntu-24_04-lts' + sku: 'server' + version: 'latest' + } + imageTemplateCustomizationSteps: [ + { + type: 'Shell' + name: 'Example script' + scriptUri: 'https://${nestedDependencies.outputs.assetsStorageAccountName}.blob.${az.environment().suffixes.storage}/${nestedDependencies.outputs.assetsStorageAccountContainerName}/${nestedDependencies.outputs.exampleScriptName}' + } + ] + } +} diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyImage/scripts/exampleScript.sh b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyImage/scripts/exampleScript.sh new file mode 100644 index 0000000000..22cd6c612f --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/tests/e2e/deployOnlyImage/scripts/exampleScript.sh @@ -0,0 +1,4 @@ +echo '###############################################' +echo '# Hey there. Just checking the place out. #' +echo '###############################################' + diff --git a/avm/ptn/virtual-machine-images/azure-image-builder/version.json b/avm/ptn/virtual-machine-images/azure-image-builder/version.json new file mode 100644 index 0000000000..83083db694 --- /dev/null +++ b/avm/ptn/virtual-machine-images/azure-image-builder/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} \ No newline at end of file diff --git a/avm/utilities/e2e-template-assets/scripts/Wait-ForImageBuild.ps1 b/avm/utilities/e2e-template-assets/scripts/Wait-ForImageBuild.ps1 new file mode 100644 index 0000000000..bce2306a21 --- /dev/null +++ b/avm/utilities/e2e-template-assets/scripts/Wait-ForImageBuild.ps1 @@ -0,0 +1,97 @@ +<# +.SYNOPSIS +Fetch the latest build status for the provided image template + +.DESCRIPTION +Fetch the latest build status for the provided image template + +.PARAMETER ResourceGroupName +Required. The name of the Resource Group containing the image template + +.PARAMETER ImageTemplateName +Required. The name of the image template to query to build status for. E.g. 'lin_it-2022-02-20-16-17-38' + +.EXAMPLE +. 'Wait-ForImageBuild.ps1' -ResourceGroupName' 'myRG' -ImageTemplateName 'lin_it-2022-02-20-16-17-38' + +Check the current build status of Image Template 'lin_it-2022-02-20-16-17-38' in Resource Group 'myRG' +#> +[CmdletBinding()] +param( + [Parameter(Mandatory)] + [string] $ResourceGroupName, + + [Parameter(Mandatory)] + [string] $ImageTemplateName +) + +begin { + Write-Debug ('[{0} entered]' -f $MyInvocation.MyCommand) +} + +process { + # Logic + # ----- + $context = Get-AzContext + $subscriptionId = $context.Subscription.Id + $currentRetry = 1 + $maximumRetries = 720 + $timeToWait = 15 + $maxTimeCalc = '{0:hh\:mm\:ss}' -f [timespan]::fromseconds($maximumRetries * $timeToWait) + do { + + # Runnning fetch in retry as it happened that the status was not available + $statusFetchRetryCount = 3 + $statusFetchCurrentRetry = 1 + do { + $path = '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.VirtualMachineImages/imageTemplates/{2}?api-version=2020-02-14' -f $subscriptionId, $ResourceGroupName, $ImageTemplateName + $requestInputObject = @{ + Method = 'GET' + Path = $path + } + + $response = ((Invoke-AzRestMethod @requestInputObject).Content | ConvertFrom-Json).properties + + if ($response.lastRunStatus) { + $latestStatus = $response.lastRunStatus + break + } + Start-Sleep 5 + $statusFetchCurrentRetry++ + } while ($statusFetchCurrentRetry -le $statusFetchRetryCount) + + if (-not $latestStatus) { + Write-Verbose ('Image Build failed with error: [{0}]' -f $response.provisioningError.message) -Verbose + $latestStatus = 'failed' + } + + + if ($latestStatus -eq 'failed' -or $latestStatus.runState.ToLower() -eq 'failed') { + $failedMessage = 'Image Template [{0}] build failed with status [{1}]. API reply: [{2}]' -f $ImageTemplateName, $latestStatus.runState, $response.lastRunStatus.message + Write-Verbose $failedMessage -Verbose + throw $failedMessage + } + + if ($latestStatus.runState.ToLower() -notIn @('running', 'new')) { + break + } + + $currTimeCalc = '{0:hh\:mm\:ss}' -f [timespan]::fromseconds($currentRetry * $timeToWait) + + Write-Verbose ('[{0}] Waiting 15 seconds [{1}|{2}]' -f (Get-Date -Format 'HH:mm:ss'), $currTimeCalc, $maxTimeCalc) -Verbose + $currentRetry++ + Start-Sleep $timeToWait + } while ($currentRetry -le $maximumRetries) + + if ($latestStatus) { + $duration = New-TimeSpan -Start $latestStatus.startTime -End $latestStatus.endTime + Write-Verbose ('It took [{0}] minutes and [{1}] seconds to build and distribute the image.' -f $duration.Minutes, $duration.Seconds) -Verbose + } else { + Write-Warning "Timeout at [$currTimeCalc]. Note, the Azure Image Builder may still succeed." + } + return $latestStatus +} + +end { + Write-Debug ('[{0} existed]' -f $MyInvocation.MyCommand) +} diff --git a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Invoke-ResourceRemoval.ps1 b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Invoke-ResourceRemoval.ps1 index b2723eaaf1..455ba34ee0 100644 --- a/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Invoke-ResourceRemoval.ps1 +++ b/avm/utilities/pipelines/e2eValidation/resourceRemoval/helper/Invoke-ResourceRemoval.ps1 @@ -147,6 +147,64 @@ function Invoke-ResourceRemoval { } break } + 'Microsoft.VirtualMachineImages/imageTemplates' { + # Note: If you ever run into the issue that you cannot remove the image template because of an issue with the MSI (e.g., because the below logic was not executed in the pipeline), you can follow these manual steps: + # 1. Unassign the existing MSI (az image builder identity remove --resource-group --name --user-assigned --yes) + # 2. Trigger image template removal (will fail, but remove the cached 'running' state) + # 3. Assign a new MSI (az image builder identity assign --resource-group --name --user-assigned ) + # 4. Trigger image template removal again, which removes the resource for good + + $resourceGroupName = $ResourceId.Split('/')[4] + $resourceName = Split-Path $ResourceId -Leaf + + # Remove resource + if ($PSCmdlet.ShouldProcess("Image Template [$resourceName]", 'Remove')) { + + $removeRequestInputObject = @{ + Method = 'DELETE' + Path = '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.VirtualMachineImages/imageTemplates/{2}?api-version=2022-07-01' -f $subscriptionId, $resourceGroupName, $resourceName + } + $removalResponse = Invoke-AzRestMethod @removeRequestInputObject + if ($removalResponse.StatusCode -notlike '2*') { + $responseContent = $removalResponse.Content | ConvertFrom-Json + throw ('{0} : {1}' -f $responseContent.error.code, $responseContent.error.message) + } + + # Wait for template to be removed. If we don't wait, it can happen that its MSI is removed too soon, locking the resource from deletion + $retryCount = 1 + $retryLimit = 240 + $retryInterval = 15 + do { + $getRequestInputObject = @{ + Method = 'GET' + Path = '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.VirtualMachineImages/imageTemplates/{2}?api-version=2022-07-01' -f $subscriptionId, $resourceGroupName, $resourceName + } + $getReponse = Invoke-AzRestMethod @getRequestInputObject + + if ($getReponse.StatusCode -eq 400) { + # Invalid request + throw ($imageTgetReponseemplate.Content | ConvertFrom-Json).error.message + } elseif ($getReponse.StatusCode -eq 404) { + # Resource not found, removal was successful + $templateExists = $false + } elseif ($getReponse.StatusCode -eq '200') { + # Resource still around - try again + $templateExists = $true + Write-Verbose (' [⏱️] Waiting {0} seconds for Image Template to be removed. [{1}/{2}]' -f $retryInterval, $retryCount, $retryLimit) -Verbose + Start-Sleep -Seconds $retryInterval + $retryCount++ + } else { + throw ('Failed request. Response: [{0}]' -f ($getReponse | Out-String)) + } + } while ($templateExists -and $retryCount -lt $retryLimit) + + if ($retryCount -ge $retryLimit) { + Write-Warning (' [!] Image Template [{0}] was not removed after {1} seconds. Continuing with resource removal.' -f $resourceName, ($retryCount * $retryInterval)) + break + } + } + break + } 'Microsoft.MachineLearningServices/workspaces' { $subscriptionId = $ResourceId.Split('/')[2] $resourceGroupName = $ResourceId.Split('/')[4] From 7876f362de2c57cd4e43b514539ffb126c8ced1a Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Wed, 18 Sep 2024 05:45:40 +0200 Subject: [PATCH 23/57] fix: Add key vault to cognitive service - avm/res/cognitive-services/account (#3222) ## Description Follow-up to: #1932 cc: @Agazoth | Pipeline | | -------- | [![avm.res.cognitive-services.account](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.cognitive-services.account.yml/badge.svg?branch=users%2Falsehr%2FAgazoth%2FAddKVToCognitiveService&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.cognitive-services.account.yml) ## Type of Change - [ ] Update to CI Environment or utlities (Non-module effecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Axel B. Andersen Co-authored-by: Javier Cevallos --- avm/res/cognitive-services/account/README.md | 137 ++++++++++-- avm/res/cognitive-services/account/main.bicep | 55 +++++ avm/res/cognitive-services/account/main.json | 207 +++++++++++++++++- .../account/modules/keyVaultExport.bicep | 62 ++++++ .../default-with-key-vault/dependencies.bicep | 21 ++ .../default-with-key-vault/main.test.bicep | 63 ++++++ 6 files changed, 530 insertions(+), 15 deletions(-) create mode 100644 avm/res/cognitive-services/account/modules/keyVaultExport.bicep create mode 100644 avm/res/cognitive-services/account/tests/e2e/default-with-key-vault/dependencies.bicep create mode 100644 avm/res/cognitive-services/account/tests/e2e/default-with-key-vault/main.test.bicep diff --git a/avm/res/cognitive-services/account/README.md b/avm/res/cognitive-services/account/README.md index 32e22ce46f..7509f836a8 100644 --- a/avm/res/cognitive-services/account/README.md +++ b/avm/res/cognitive-services/account/README.md @@ -20,6 +20,7 @@ This module deploys a Cognitive Service. | `Microsoft.CognitiveServices/accounts` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.CognitiveServices/2023-05-01/accounts) | | `Microsoft.CognitiveServices/accounts/deployments` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.CognitiveServices/2023-05-01/accounts/deployments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.KeyVault/vaults/secrets` | [2023-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2023-07-01/vaults/secrets) | | `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | | `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | @@ -33,13 +34,14 @@ The following section provides usage examples for the module, which were used to - [Using `AIServices` with `deployments` in parameter set and private endpoints](#example-1-using-aiservices-with-deployments-in-parameter-set-and-private-endpoints) - [Using `AIServices` with `deployments` in parameter set](#example-2-using-aiservices-with-deployments-in-parameter-set) -- [Using only defaults](#example-3-using-only-defaults) -- [Using large parameter set](#example-4-using-large-parameter-set) -- [Using `OpenAI` and `deployments` in parameter set with private endpoint](#example-5-using-openai-and-deployments-in-parameter-set-with-private-endpoint) -- [As Speech Service](#example-6-as-speech-service) -- [Using Customer-Managed-Keys with System-Assigned identity](#example-7-using-customer-managed-keys-with-system-assigned-identity) -- [Using Customer-Managed-Keys with User-Assigned identity](#example-8-using-customer-managed-keys-with-user-assigned-identity) -- [WAF-aligned](#example-9-waf-aligned) +- [Storing keys of service in key vault](#example-3-storing-keys-of-service-in-key-vault) +- [Using only defaults](#example-4-using-only-defaults) +- [Using large parameter set](#example-5-using-large-parameter-set) +- [Using `OpenAI` and `deployments` in parameter set with private endpoint](#example-6-using-openai-and-deployments-in-parameter-set-with-private-endpoint) +- [As Speech Service](#example-7-as-speech-service) +- [Using Customer-Managed-Keys with System-Assigned identity](#example-8-using-customer-managed-keys-with-system-assigned-identity) +- [Using Customer-Managed-Keys with User-Assigned identity](#example-9-using-customer-managed-keys-with-user-assigned-identity) +- [WAF-aligned](#example-10-waf-aligned) ### Example 1: _Using `AIServices` with `deployments` in parameter set and private endpoints_ @@ -237,7 +239,71 @@ module account 'br/public:avm/res/cognitive-services/account:' = {

-### Example 3: _Using only defaults_ +### Example 3: _Storing keys of service in key vault_ + +This instance deploys the module and stores its keys in a key vault. + + +

+ +via Bicep module + +```bicep +module account 'br/public:avm/res/cognitive-services/account:' = { + name: 'accountDeployment' + params: { + // Required parameters + kind: 'SpeechServices' + name: 'csakv001' + // Non-required parameters + location: '' + secretsExportConfiguration: { + accessKey1Name: 'csakv001-accessKey1' + accessKey2Name: 'csakv001-accessKey2' + keyVaultResourceId: '' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "kind": { + "value": "SpeechServices" + }, + "name": { + "value": "csakv001" + }, + // Non-required parameters + "location": { + "value": "" + }, + "secretsExportConfiguration": { + "value": { + "accessKey1Name": "csakv001-accessKey1", + "accessKey2Name": "csakv001-accessKey2", + "keyVaultResourceId": "" + } + } + } +} +``` + +
+

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

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

-### Example 5: _Using `OpenAI` and `deployments` in parameter set with private endpoint_ +### Example 6: _Using `OpenAI` and `deployments` in parameter set with private endpoint_ This instance deploys the module with the AI model deployment feature and private endpoint. @@ -689,7 +755,7 @@ module account 'br/public:avm/res/cognitive-services/account:' = {

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

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

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

-### Example 9: _WAF-aligned_ +### Example 10: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -1146,6 +1212,7 @@ module account 'br/public:avm/res/cognitive-services/account:' = { | [`restore`](#parameter-restore) | bool | Restore a soft-deleted cognitive service at deployment time. Will fail if no such soft-deleted resource exists. | | [`restrictOutboundNetworkAccess`](#parameter-restrictoutboundnetworkaccess) | bool | Restrict outbound network access. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`secretsExportConfiguration`](#parameter-secretsexportconfiguration) | object | Key vault reference and secret settings for the module's secrets export. | | [`sku`](#parameter-sku) | string | SKU of the Cognitive Services resource. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region. | | [`tags`](#parameter-tags) | object | Tags of the resource. | | [`userOwnedStorage`](#parameter-userownedstorage) | array | The storage accounts for this resource. | @@ -2142,6 +2209,47 @@ The principal type of the assigned principal ID. ] ``` +### Parameter: `secretsExportConfiguration` + +Key vault reference and secret settings for the module's secrets export. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`keyVaultResourceId`](#parameter-secretsexportconfigurationkeyvaultresourceid) | string | The key vault name where to store the keys and connection strings generated by the modules. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`accessKey1Name`](#parameter-secretsexportconfigurationaccesskey1name) | string | The name for the accessKey1 secret to create. | +| [`accessKey2Name`](#parameter-secretsexportconfigurationaccesskey2name) | string | The name for the accessKey2 secret to create. | + +### Parameter: `secretsExportConfiguration.keyVaultResourceId` + +The key vault name where to store the keys and connection strings generated by the modules. + +- Required: Yes +- Type: string + +### Parameter: `secretsExportConfiguration.accessKey1Name` + +The name for the accessKey1 secret to create. + +- Required: No +- Type: string + +### Parameter: `secretsExportConfiguration.accessKey2Name` + +The name for the accessKey2 secret to create. + +- Required: No +- Type: string + ### Parameter: `sku` SKU of the Cognitive Services resource. Use 'Get-AzCognitiveServicesAccountSku' to determine a valid combinations of 'kind' and 'SKU' for your Azure region. @@ -2192,6 +2300,7 @@ The storage accounts for this resource. | :-- | :-- | :-- | | `endpoint` | string | The service endpoint of the cognitive services account. | | `endpoints` | | All endpoints available for the cognitive services account, types depends on the cognitive service kind. | +| `exportedSecrets` | | A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name. | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the cognitive services account. | | `resourceGroupName` | string | The resource group the cognitive services account was deployed into. | diff --git a/avm/res/cognitive-services/account/main.bicep b/avm/res/cognitive-services/account/main.bicep index 0080ac6d3a..eb12753477 100644 --- a/avm/res/cognitive-services/account/main.bicep +++ b/avm/res/cognitive-services/account/main.bicep @@ -123,6 +123,9 @@ param enableTelemetry bool = true @description('Optional. Array of deployments about cognitive service accounts to create.') param deployments deploymentsType +@description('Optional. Key vault reference and secret settings for the module\'s secrets export.') +param secretsExportConfiguration secretsExportConfigurationType? + var formattedUserAssignedIdentities = reduce( map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), {}, @@ -468,6 +471,36 @@ resource cognitiveService_roleAssignments 'Microsoft.Authorization/roleAssignmen } ] +module secretsExport 'modules/keyVaultExport.bicep' = if (secretsExportConfiguration != null) { + name: '${uniqueString(deployment().name, location)}-secrets-kv' + scope: resourceGroup( + split((secretsExportConfiguration.?keyVaultResourceId ?? '//'), '/')[2], + split((secretsExportConfiguration.?keyVaultResourceId ?? '////'), '/')[4] + ) + params: { + keyVaultName: last(split(secretsExportConfiguration.?keyVaultResourceId ?? '//', '/')) + secretsToSet: union( + [], + contains(secretsExportConfiguration!, 'accessKey1Name') + ? [ + { + name: secretsExportConfiguration!.accessKey1Name + value: cognitiveService.listKeys().key1 + } + ] + : [], + contains(secretsExportConfiguration!, 'accessKey2Name') + ? [ + { + name: secretsExportConfiguration!.accessKey2Name + value: cognitiveService.listKeys().key2 + } + ] + : [] + ) + } +} + @description('The name of the cognitive services account.') output name string = cognitiveService.name @@ -489,6 +522,11 @@ output systemAssignedMIPrincipalId string = cognitiveService.?identity.?principa @description('The location the resource was deployed into.') output location string = cognitiveService.location +@description('A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret\'s name.') +output exportedSecrets secretsOutputType = (secretsExportConfiguration != null) + ? toObject(secretsExport.outputs.secretsSet, secret => last(split(secret.secretResourceId, '/')), secret => secret) + : {} + // ================ // // Definitions // // ================ // @@ -706,3 +744,20 @@ type endpointsType = { @description('The endpoint URI.') endpoint: string? }? + +type secretsExportConfigurationType = { + @description('Required. The key vault name where to store the keys and connection strings generated by the modules.') + keyVaultResourceId: string + + @description('Optional. The name for the accessKey1 secret to create.') + accessKey1Name: string? + + @description('Optional. The name for the accessKey2 secret to create.') + accessKey2Name: string? +} + +import { secretSetType } from 'modules/keyVaultExport.bicep' +type secretsOutputType = { + @description('An exported secret\'s references.') + *: secretSetType +} diff --git a/avm/res/cognitive-services/account/main.json b/avm/res/cognitive-services/account/main.json index 2767bbb995..27a5d3ba80 100644 --- a/avm/res/cognitive-services/account/main.json +++ b/avm/res/cognitive-services/account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "14735378599748380229" + "templateHash": "259342811715055071" }, "name": "Cognitive Services", "description": "This module deploys a Cognitive Service.", @@ -573,6 +573,63 @@ } }, "nullable": true + }, + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The key vault name where to store the keys and connection strings generated by the modules." + } + }, + "accessKey1Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey1 secret to create." + } + }, + "accessKey2Name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name for the accessKey2 secret to create." + } + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/secretSetType", + "metadata": { + "description": "An exported secret's references." + } + } + }, + "secretSetType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "modules/keyVaultExport.bicep" + } + } } }, "parameters": { @@ -783,6 +840,13 @@ "metadata": { "description": "Optional. Array of deployments about cognitive service accounts to create." } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } } }, "variables": { @@ -1692,6 +1756,140 @@ "dependsOn": [ "cognitiveService" ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-secrets-kv', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '////'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1Name'), createArray(createObject('name', parameters('secretsExportConfiguration').accessKey1Name, 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key1)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2Name'), createArray(createObject('name', parameters('secretsExportConfiguration').accessKey2Name, 'value', listKeys(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), '2023-05-01').key2)), createArray()))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "986606208324987345" + } + }, + "definitions": { + "secretSetType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "secretToSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the ecrets in." + } + }, + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" + }, + "metadata": { + "description": "Required. The secrets to set in the Key Vault." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" + }, + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" + }, + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + }, + "dependsOn": [ + "keyVault" + ] + } + }, + "outputs": { + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetType" + }, + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]" + } + } + } + } + } + }, + "dependsOn": [ + "cognitiveService" + ] } }, "outputs": { @@ -1743,6 +1941,13 @@ "description": "The location the resource was deployed into." }, "value": "[reference('cognitiveService', '2023-05-01', 'full').location]" + }, + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" } } } \ No newline at end of file diff --git a/avm/res/cognitive-services/account/modules/keyVaultExport.bicep b/avm/res/cognitive-services/account/modules/keyVaultExport.bicep new file mode 100644 index 0000000000..d537d2407e --- /dev/null +++ b/avm/res/cognitive-services/account/modules/keyVaultExport.bicep @@ -0,0 +1,62 @@ +// ============== // +// Parameters // +// ============== // + +@description('Required. The name of the Key Vault to set the ecrets in.') +param keyVaultName string + +@description('Required. The secrets to set in the Key Vault.') +param secretsToSet secretToSetType[] + +// ============= // +// Resources // +// ============= // + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +resource secrets 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = [ + for secret in secretsToSet: { + name: secret.name + parent: keyVault + properties: { + value: secret.value + } + } +] + +// =========== // +// Outputs // +// =========== // + +@description('The references to the secrets exported to the provided Key Vault.') +output secretsSet secretSetType[] = [ + #disable-next-line outputs-should-not-contain-secrets // Only returning the references, not a secret value + for index in range(0, length(secretsToSet ?? [])): { + secretResourceId: secrets[index].id + secretUri: secrets[index].properties.secretUri + } +] + +// =============== // +// Definitions // +// =============== // + +@export() +type secretSetType = { + @description('The resourceId of the exported secret.') + secretResourceId: string + + @description('The secret URI of the exported secret.') + secretUri: string +} + +type secretToSetType = { + @description('Required. The name of the secret to set.') + name: string + + @description('Required. The value of the secret to set.') + @secure() + value: string +} diff --git a/avm/res/cognitive-services/account/tests/e2e/default-with-key-vault/dependencies.bicep b/avm/res/cognitive-services/account/tests/e2e/default-with-key-vault/dependencies.bicep new file mode 100644 index 0000000000..61c051d86d --- /dev/null +++ b/avm/res/cognitive-services/account/tests/e2e/default-with-key-vault/dependencies.bicep @@ -0,0 +1,21 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param keyVaultName string + +resource keyVault 'Microsoft.KeyVault/vaults@2021-06-01-preview' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + enableRbacAuthorization: true + tenantId: subscription().tenantId + } +} + +@description('The name of the Key Vault created.') +output keyVaultResourceId string = keyVault.id diff --git a/avm/res/cognitive-services/account/tests/e2e/default-with-key-vault/main.test.bicep b/avm/res/cognitive-services/account/tests/e2e/default-with-key-vault/main.test.bicep new file mode 100644 index 0000000000..faeae8df01 --- /dev/null +++ b/avm/res/cognitive-services/account/tests/e2e/default-with-key-vault/main.test.bicep @@ -0,0 +1,63 @@ +targetScope = 'subscription' + +metadata name = 'Storing keys of service in key vault' +metadata description = 'This instance deploys the module and stores its keys in a key vault.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cognitiveservices.accounts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'csakv' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + location: resourceLocation + } +} + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + kind: 'SpeechServices' + location: resourceLocation + secretsExportConfiguration: { + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + accessKey1Name: '${namePrefix}${serviceShort}001-accessKey1' + accessKey2Name: '${namePrefix}${serviceShort}001-accessKey2' + } + } + } +] From 180cc1a06900161acf80790d3728cd9012f3645d Mon Sep 17 00:00:00 2001 From: John Date: Wed, 18 Sep 2024 20:16:59 +0200 Subject: [PATCH 24/57] feat: Added new resource module Managed DevOps Pools - `avm/res/dev-ops-infrastructure/pool` (#2894) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description This PR introduces the new Managed DevOps Pools as requested in https://github.com/Azure/Azure-Verified-Modules/issues/1217. Adding @AlexanderSehr and @surajguptha for code review ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.dev-ops-infrastructure.pool](https://github.com/johnlokerse/bicep-registry-modules/actions/workflows/avm.res.dev-ops-infrastructure.pool.yml/badge.svg?branch=johnlokerse%2Fimplement-mdp-module&event=workflow_dispatch)](https://github.com/johnlokerse/bicep-registry-modules/actions/workflows/avm.res.dev-ops-infrastructure.pool.yml) | ## Type of Change - [x] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Máté Barabás --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + .../avm.res.dev-ops-infrastructure.pool.yml | 88 ++ avm/res/dev-ops-infrastructure/pool/README.md | 1320 +++++++++++++++++ .../dev-ops-infrastructure/pool/main.bicep | 489 ++++++ avm/res/dev-ops-infrastructure/pool/main.json | 1013 +++++++++++++ .../tests/e2e/defaults/dependencies.bicep | 27 + .../pool/tests/e2e/defaults/main.test.bicep | 78 + .../pool/tests/e2e/max/dependencies.bicep | 89 ++ .../pool/tests/e2e/max/main.test.bicep | 171 +++ .../tests/e2e/waf-aligned/dependencies.bicep | 89 ++ .../tests/e2e/waf-aligned/main.test.bicep | 127 ++ .../dev-ops-infrastructure/pool/version.json | 7 + 13 files changed, 3500 insertions(+) create mode 100644 .github/workflows/avm.res.dev-ops-infrastructure.pool.yml create mode 100644 avm/res/dev-ops-infrastructure/pool/README.md create mode 100644 avm/res/dev-ops-infrastructure/pool/main.bicep create mode 100644 avm/res/dev-ops-infrastructure/pool/main.json create mode 100644 avm/res/dev-ops-infrastructure/pool/tests/e2e/defaults/dependencies.bicep create mode 100644 avm/res/dev-ops-infrastructure/pool/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/dev-ops-infrastructure/pool/tests/e2e/max/dependencies.bicep create mode 100644 avm/res/dev-ops-infrastructure/pool/tests/e2e/max/main.test.bicep create mode 100644 avm/res/dev-ops-infrastructure/pool/tests/e2e/waf-aligned/dependencies.bicep create mode 100644 avm/res/dev-ops-infrastructure/pool/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/dev-ops-infrastructure/pool/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f67f52ec14..fff88d76bc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -62,6 +62,7 @@ /avm/res/desktop-virtualization/host-pool/ @Azure/avm-res-desktopvirtualization-hostpool-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/desktop-virtualization/scaling-plan/ @Azure/avm-res-desktopvirtualization-scalingplan-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/desktop-virtualization/workspace/ @Azure/avm-res-desktopvirtualization-workspace-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/res/dev-ops-infrastructure/pool/ @Azure/avm-res-devopsinfrastructure-pool-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/dev-test-lab/lab/ @Azure/avm-res-devtestlab-lab-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/digital-twins/digital-twins-instance/ @Azure/avm-res-digitaltwins-digitaltwinsinstance-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/document-db/database-account/ @Azure/avm-res-documentdb-databaseaccount-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index d30ee744be..feded8d600 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -97,6 +97,7 @@ body: - "avm/res/desktop-virtualization/host-pool" - "avm/res/desktop-virtualization/scaling-plan" - "avm/res/desktop-virtualization/workspace" + - "avm/res/dev-ops-infrastructure/pool" - "avm/res/dev-test-lab/lab" - "avm/res/digital-twins/digital-twins-instance" - "avm/res/document-db/database-account" diff --git a/.github/workflows/avm.res.dev-ops-infrastructure.pool.yml b/.github/workflows/avm.res.dev-ops-infrastructure.pool.yml new file mode 100644 index 0000000000..dac40abeb8 --- /dev/null +++ b/.github/workflows/avm.res.dev-ops-infrastructure.pool.yml @@ -0,0 +1,88 @@ +name: "avm.res.dev-ops-infrastructure.pool" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.dev-ops-infrastructure.pool.yml" + - "avm/res/dev-ops-infrastructure/pool/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/dev-ops-infrastructure/pool" + workflowPath: ".github/workflows/avm.res.dev-ops-infrastructure.pool.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/dev-ops-infrastructure/pool/README.md b/avm/res/dev-ops-infrastructure/pool/README.md new file mode 100644 index 0000000000..4cd888f4b4 --- /dev/null +++ b/avm/res/dev-ops-infrastructure/pool/README.md @@ -0,0 +1,1320 @@ +# Managed DevOps Pool `[Microsoft.DevOpsInfrastructure/pools]` + +This module deploys the Managed DevOps Pool resource. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.DevOpsInfrastructure/pools` | [2024-04-04-preview](https://learn.microsoft.com/en-us/azure/templates) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | + +## Notes + +The Managed DevOps Pool resource requires external permissions in Azure DevOps. Make sure that the deployment principal has permission in Azure DevOps: [Managed DevOps Pools - Verify Azure DevOps Permissions](https://learn.microsoft.com/en-us/azure/devops/managed-devops-pools/prerequisites?view=azure-devops&tabs=azure-portal#verify-azure-devops-permissions) + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/dev-ops-infrastructure/pool:`. + +- [Using only defaults](#example-1-using-only-defaults) +- [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module pool 'br/public:avm/res/dev-ops-infrastructure/pool:' = { + name: 'poolDeployment' + params: { + // Required parameters + agentProfile: { + kind: 'Stateless' + } + concurrency: 1 + devCenterProjectResourceId: '' + fabricProfileSkuName: 'Standard_DS2_v2' + images: [ + { + wellKnownImageName: 'windows-2022/latest' + } + ] + name: 'mdpmin001' + organizationProfile: { + kind: 'AzureDevOps' + organizations: [ + { + url: '' + } + ] + } + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "agentProfile": { + "value": { + "kind": "Stateless" + } + }, + "concurrency": { + "value": 1 + }, + "devCenterProjectResourceId": { + "value": "" + }, + "fabricProfileSkuName": { + "value": "Standard_DS2_v2" + }, + "images": { + "value": [ + { + "wellKnownImageName": "windows-2022/latest" + } + ] + }, + "name": { + "value": "mdpmin001" + }, + "organizationProfile": { + "value": { + "kind": "AzureDevOps", + "organizations": [ + { + "url": "" + } + ] + } + }, + // Non-required parameters + "location": { + "value": "" + } + } +} +``` + +
+

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

+ +via Bicep module + +```bicep +module pool 'br/public:avm/res/dev-ops-infrastructure/pool:' = { + name: 'poolDeployment' + params: { + // Required parameters + agentProfile: { + kind: 'Stateless' + resourcePredictions: { + daysData: [ + { + '09:00:00': 1 + '17:00:00': 0 + } + {} + {} + {} + { + '09:00:00': 1 + '17:00:00': 0 + } + {} + {} + ] + timeZone: 'Central Europe Standard Time' + } + resourcePredictionsProfile: { + kind: 'Automatic' + predictionPreference: 'Balanced' + } + } + concurrency: 1 + devCenterProjectResourceId: '' + fabricProfileSkuName: 'Standard_D2_v2' + images: [ + { + aliases: [ + 'windows-2022' + ] + buffer: '*' + wellKnownImageName: 'windows-2022/latest' + } + ] + name: 'mdpmax001' + organizationProfile: { + kind: 'AzureDevOps' + organizations: [ + { + parallelism: 1 + projects: [ + '' + ] + url: '' + } + ] + permissionProfile: { + kind: 'CreatorOnly' + } + } + // Non-required parameters + location: '' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + roleAssignments: [ + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + ] + storageProfile: { + dataDisks: [ + { + caching: 'ReadWrite' + diskSizeGiB: 100 + driveLetter: 'B' + storageAccountType: 'Standard_LRS' + } + ] + osDiskStorageAccountType: 'Standard' + } + subnetResourceId: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "agentProfile": { + "value": { + "kind": "Stateless", + "resourcePredictions": { + "daysData": [ + { + "09:00:00": 1, + "17:00:00": 0 + }, + {}, + {}, + {}, + { + "09:00:00": 1, + "17:00:00": 0 + }, + {}, + {} + ], + "timeZone": "Central Europe Standard Time" + }, + "resourcePredictionsProfile": { + "kind": "Automatic", + "predictionPreference": "Balanced" + } + } + }, + "concurrency": { + "value": 1 + }, + "devCenterProjectResourceId": { + "value": "" + }, + "fabricProfileSkuName": { + "value": "Standard_D2_v2" + }, + "images": { + "value": [ + { + "aliases": [ + "windows-2022" + ], + "buffer": "*", + "wellKnownImageName": "windows-2022/latest" + } + ] + }, + "name": { + "value": "mdpmax001" + }, + "organizationProfile": { + "value": { + "kind": "AzureDevOps", + "organizations": [ + { + "parallelism": 1, + "projects": [ + "" + ], + "url": "" + } + ], + "permissionProfile": { + "kind": "CreatorOnly" + } + } + }, + // Non-required parameters + "location": { + "value": "" + }, + "lock": { + "value": { + "kind": "CanNotDelete", + "name": "myCustomLockName" + } + }, + "roleAssignments": { + "value": [ + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Owner" + } + ] + }, + "storageProfile": { + "value": { + "dataDisks": [ + { + "caching": "ReadWrite", + "diskSizeGiB": 100, + "driveLetter": "B", + "storageAccountType": "Standard_LRS" + } + ], + "osDiskStorageAccountType": "Standard" + } + }, + "subnetResourceId": { + "value": "" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

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

+ +via Bicep module + +```bicep +module pool 'br/public:avm/res/dev-ops-infrastructure/pool:' = { + name: 'poolDeployment' + params: { + // Required parameters + agentProfile: { + kind: 'Stateless' + resourcePredictions: { + daysData: [ + { + '09:00:00': 1 + '17:00:00': 0 + } + {} + {} + {} + { + '09:00:00': 1 + '17:00:00': 0 + } + {} + {} + ] + timeZone: 'Central Europe Standard Time' + } + resourcePredictionsProfile: { + kind: 'Automatic' + predictionPreference: 'Balanced' + } + } + concurrency: 1 + devCenterProjectResourceId: '' + fabricProfileSkuName: 'Standard_D2_v2' + images: [ + { + wellKnownImageName: 'windows-2022/latest' + } + ] + name: 'mdpwaf001' + organizationProfile: { + kind: 'AzureDevOps' + organizations: [ + { + parallelism: 1 + projects: [ + '' + ] + url: '' + } + ] + permissionProfile: { + kind: 'CreatorOnly' + } + } + // Non-required parameters + location: '' + subnetResourceId: '' + } +} +``` + +
+

+ +

+ +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 + "agentProfile": { + "value": { + "kind": "Stateless", + "resourcePredictions": { + "daysData": [ + { + "09:00:00": 1, + "17:00:00": 0 + }, + {}, + {}, + {}, + { + "09:00:00": 1, + "17:00:00": 0 + }, + {}, + {} + ], + "timeZone": "Central Europe Standard Time" + }, + "resourcePredictionsProfile": { + "kind": "Automatic", + "predictionPreference": "Balanced" + } + } + }, + "concurrency": { + "value": 1 + }, + "devCenterProjectResourceId": { + "value": "" + }, + "fabricProfileSkuName": { + "value": "Standard_D2_v2" + }, + "images": { + "value": [ + { + "wellKnownImageName": "windows-2022/latest" + } + ] + }, + "name": { + "value": "mdpwaf001" + }, + "organizationProfile": { + "value": { + "kind": "AzureDevOps", + "organizations": [ + { + "parallelism": 1, + "projects": [ + "" + ], + "url": "" + } + ], + "permissionProfile": { + "kind": "CreatorOnly" + } + } + }, + // Non-required parameters + "location": { + "value": "" + }, + "subnetResourceId": { + "value": "" + } + } +} +``` + +
+

+ + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`agentProfile`](#parameter-agentprofile) | object | Defines how the machine will be handled once it executed a job. | +| [`concurrency`](#parameter-concurrency) | int | Defines how many resources can there be created at any given time. | +| [`devCenterProjectResourceId`](#parameter-devcenterprojectresourceid) | string | The resource id of the DevCenter Project the pool belongs to. | +| [`fabricProfileSkuName`](#parameter-fabricprofileskuname) | string | The Azure SKU name of the machines in the pool. | +| [`images`](#parameter-images) | array | The VM images of the machines in the pool. | +| [`name`](#parameter-name) | string | Name of the pool. It needs to be globally unique. | +| [`organizationProfile`](#parameter-organizationprofile) | object | Defines the organization in which the pool will be used. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`location`](#parameter-location) | string | The geo-location where the resource lives. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`managedIdentities`](#parameter-managedidentities) | object | The managed service identities assigned to this resource. | +| [`osProfile`](#parameter-osprofile) | object | The OS profile of the agents in the pool. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`storageProfile`](#parameter-storageprofile) | object | The storage profile of the machines in the pool. | +| [`subnetResourceId`](#parameter-subnetresourceid) | string | The subnet id on which to put all machines created in the pool. | +| [`tags`](#parameter-tags) | object | Tags of the resource. | + +### Parameter: `agentProfile` + +Defines how the machine will be handled once it executed a job. + +- Required: Yes +- Type: object + +### Parameter: `concurrency` + +Defines how many resources can there be created at any given time. + +- Required: Yes +- Type: int + +### Parameter: `devCenterProjectResourceId` + +The resource id of the DevCenter Project the pool belongs to. + +- Required: Yes +- Type: string + +### Parameter: `fabricProfileSkuName` + +The Azure SKU name of the machines in the pool. + +- Required: Yes +- Type: string + +### Parameter: `images` + +The VM images of the machines in the pool. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`wellKnownImageName`](#parameter-imageswellknownimagename) | string | The image to use from a well-known set of images made available to customers. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`aliases`](#parameter-imagesaliases) | array | List of aliases to reference the image by. | +| [`buffer`](#parameter-imagesbuffer) | string | The percentage of the buffer to be allocated to this image. | +| [`resourceId`](#parameter-imagesresourceid) | string | The resource id of the image. | + +### Parameter: `images.wellKnownImageName` + +The image to use from a well-known set of images made available to customers. + +- Required: Yes +- Type: string + +### Parameter: `images.aliases` + +List of aliases to reference the image by. + +- Required: No +- Type: array + +### Parameter: `images.buffer` + +The percentage of the buffer to be allocated to this image. + +- Required: No +- Type: string + +### Parameter: `images.resourceId` + +The resource id of the image. + +- Required: No +- Type: string + +### Parameter: `name` + +Name of the pool. It needs to be globally unique. + +- Required: Yes +- Type: string + +### Parameter: `organizationProfile` + +Defines the organization in which the pool will be used. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-organizationprofilekind) | string | Azure DevOps organization profile. | +| [`organizations`](#parameter-organizationprofileorganizations) | array | The list of Azure DevOps organizations the pool should be present in.. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`permissionProfile`](#parameter-organizationprofilepermissionprofile) | object | The type of permission which determines which accounts are admins on the Azure DevOps pool. | + +### Parameter: `organizationProfile.kind` + +Azure DevOps organization profile. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDevOps' + ] + ``` + +### Parameter: `organizationProfile.organizations` + +The list of Azure DevOps organizations the pool should be present in.. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`url`](#parameter-organizationprofileorganizationsurl) | string | The Azure DevOps organization URL in which the pool should be created. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`parallelism`](#parameter-organizationprofileorganizationsparallelism) | int | How many machines can be created at maximum in this organization out of the maximumConcurrency of the pool. | +| [`projects`](#parameter-organizationprofileorganizationsprojects) | array | List of projects in which the pool should be created. | + +### Parameter: `organizationProfile.organizations.url` + +The Azure DevOps organization URL in which the pool should be created. + +- Required: Yes +- Type: string + +### Parameter: `organizationProfile.organizations.parallelism` + +How many machines can be created at maximum in this organization out of the maximumConcurrency of the pool. + +- Required: No +- Type: int + +### Parameter: `organizationProfile.organizations.projects` + +List of projects in which the pool should be created. + +- Required: No +- Type: array + +### Parameter: `organizationProfile.permissionProfile` + +The type of permission which determines which accounts are admins on the Azure DevOps pool. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`groups`](#parameter-organizationprofilepermissionprofilegroups) | array | Group email addresses. | +| [`kind`](#parameter-organizationprofilepermissionprofilekind) | string | Determines who has admin permissions to the Azure DevOps pool. | +| [`users`](#parameter-organizationprofilepermissionprofileusers) | array | User email addresses. | + +### Parameter: `organizationProfile.permissionProfile.groups` + +Group email addresses. + +- Required: No +- Type: array + +### Parameter: `organizationProfile.permissionProfile.kind` + +Determines who has admin permissions to the Azure DevOps pool. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CreatorOnly' + 'Inherit' + 'SpecificAccounts' + ] + ``` + +### Parameter: `organizationProfile.permissionProfile.users` + +User email addresses. + +- Required: No +- Type: array + +### Parameter: `diagnosticSettings` + +The diagnostic settings of the service. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| [`eventHubName`](#parameter-diagnosticsettingseventhubname) | string | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | +| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. | +| [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | +| [`metricCategories`](#parameter-diagnosticsettingsmetriccategories) | array | The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. | +| [`name`](#parameter-diagnosticsettingsname) | string | The name of diagnostic setting. | +| [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | + +### Parameter: `diagnosticSettings.eventHubAuthorizationRuleResourceId` + +Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.eventHubName` + +Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `diagnosticSettings.logCategoriesAndGroups` + +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. | +| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. | +| [`enabled`](#parameter-diagnosticsettingslogcategoriesandgroupsenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.category` + +Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.categoryGroup` + +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.metricCategories` + +The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingsmetriccategoriescategory) | string | Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enabled`](#parameter-diagnosticsettingsmetriccategoriesenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.metricCategories.category` + +Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. + +- Required: Yes +- Type: string + +### Parameter: `diagnosticSettings.metricCategories.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.name` + +The name of diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.storageAccountResourceId` + +Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.workspaceResourceId` + +Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `location` + +The geo-location where the resource lives. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `managedIdentities` + +The managed service identities assigned to this resource. + +- Required: No +- Type: object +- Example: + ```Bicep + { + systemAssigned: true, + userAssignedResourceIds: [ + '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity' + ] + } + { + systemAssigned: true + } + ``` + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`systemAssigned`](#parameter-managedidentitiessystemassigned) | bool | Enables system assigned managed identity on the resource. | +| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. | + +### Parameter: `managedIdentities.systemAssigned` + +Enables system assigned managed identity on the resource. + +- Required: No +- Type: bool + +### Parameter: `managedIdentities.userAssignedResourceIds` + +The resource ID(s) to assign to the resource. + +- Required: No +- Type: array + +### Parameter: `osProfile` + +The OS profile of the agents in the pool. + +- Required: No +- Type: object +- Default: + ```Bicep + { + logonType: 'Interactive' + secretsManagementSettings: { + keyExportable: false + observedCertificates: [] + } + } + ``` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`logonType`](#parameter-osprofilelogontype) | string | The logon type of the machine. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`secretsManagementSettings`](#parameter-osprofilesecretsmanagementsettings) | object | The secret management settings of the machines in the pool. | + +### Parameter: `osProfile.logonType` + +The logon type of the machine. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Interactive' + 'Service' + ] + ``` + +### Parameter: `osProfile.secretsManagementSettings` + +The secret management settings of the machines in the pool. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`keyExportable`](#parameter-osprofilesecretsmanagementsettingskeyexportable) | bool | The secret management settings of the machines in the pool. | +| [`observedCertificates`](#parameter-osprofilesecretsmanagementsettingsobservedcertificates) | array | The list of certificates to install on all machines in the pool. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`certificateStoreLocation`](#parameter-osprofilesecretsmanagementsettingscertificatestorelocation) | string | Where to store certificates on the machine. | + +### Parameter: `osProfile.secretsManagementSettings.keyExportable` + +The secret management settings of the machines in the pool. + +- Required: Yes +- Type: bool + +### Parameter: `osProfile.secretsManagementSettings.observedCertificates` + +The list of certificates to install on all machines in the pool. + +- Required: Yes +- Type: array + +### Parameter: `osProfile.secretsManagementSettings.certificateStoreLocation` + +Where to store certificates on the machine. + +- Required: No +- Type: string + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". | +| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `storageProfile` + +The storage profile of the machines in the pool. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`dataDisks`](#parameter-storageprofiledatadisks) | array | A list of empty data disks to attach. | +| [`osDiskStorageAccountType`](#parameter-storageprofileosdiskstorageaccounttype) | string | The Azure SKU name of the machines in the pool. | + +### Parameter: `storageProfile.dataDisks` + +A list of empty data disks to attach. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`caching`](#parameter-storageprofiledatadiskscaching) | string | The type of caching to be enabled for the data disks. The default value for caching is readwrite. For information about the caching options see: https://blogs.msdn.microsoft.com/windowsazurestorage/2012/06/27/exploring-windows-azure-drives-disks-and-images/. | +| [`diskSizeGiB`](#parameter-storageprofiledatadisksdisksizegib) | int | The initial disk size in gigabytes. | +| [`driveLetter`](#parameter-storageprofiledatadisksdriveletter) | string | The drive letter for the empty data disk. If not specified, it will be the first available letter. Letters A, C, D, and E are not allowed. | +| [`storageAccountType`](#parameter-storageprofiledatadisksstorageaccounttype) | string | The storage Account type to be used for the data disk. If omitted, the default is Standard_LRS. | + +### Parameter: `storageProfile.dataDisks.caching` + +The type of caching to be enabled for the data disks. The default value for caching is readwrite. For information about the caching options see: https://blogs.msdn.microsoft.com/windowsazurestorage/2012/06/27/exploring-windows-azure-drives-disks-and-images/. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'None' + 'ReadOnly' + 'ReadWrite' + ] + ``` + +### Parameter: `storageProfile.dataDisks.diskSizeGiB` + +The initial disk size in gigabytes. + +- Required: No +- Type: int + +### Parameter: `storageProfile.dataDisks.driveLetter` + +The drive letter for the empty data disk. If not specified, it will be the first available letter. Letters A, C, D, and E are not allowed. + +- Required: No +- Type: string + +### Parameter: `storageProfile.dataDisks.storageAccountType` + +The storage Account type to be used for the data disk. If omitted, the default is Standard_LRS. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Premium_LRS' + 'Premium_ZRS' + 'Standard_LRS' + 'StandardSSD_LRS' + 'StandardSSD_ZRS' + ] + ``` + +### Parameter: `storageProfile.osDiskStorageAccountType` + +The Azure SKU name of the machines in the pool. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Premium' + 'Standard' + 'StandardSSD' + ] + ``` + +### Parameter: `subnetResourceId` + +The subnet id on which to put all machines created in the pool. + +- Required: No +- Type: string + +### Parameter: `tags` + +Tags of the resource. + +- Required: No +- Type: object + + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `location` | string | The location the Managed DevOps Pool resource was deployed into. | +| `name` | string | The name of the Managed DevOps Pool. | +| `resourceGroupName` | string | The name of the resource group the Managed DevOps Pool resource was deployed into. | +| `resourceId` | string | The resource ID of the Managed DevOps Pool. | +| `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | + +## Cross-referenced modules + +_None_ + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/dev-ops-infrastructure/pool/main.bicep b/avm/res/dev-ops-infrastructure/pool/main.bicep new file mode 100644 index 0000000000..c64d2bbce5 --- /dev/null +++ b/avm/res/dev-ops-infrastructure/pool/main.bicep @@ -0,0 +1,489 @@ +metadata name = 'Managed DevOps Pool' +metadata description = 'This module deploys the Managed DevOps Pool resource.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. Name of the pool. It needs to be globally unique.') +param name string + +@description('Required. The Azure SKU name of the machines in the pool.') +param fabricProfileSkuName string + +@minValue(1) +@maxValue(10000) +@description('Required. Defines how many resources can there be created at any given time.') +param concurrency int + +@description('Required. The VM images of the machines in the pool.') +param images imageType + +@description('Optional. The geo-location where the resource lives.') +param location string = resourceGroup().location + +@description('Required. The resource id of the DevCenter Project the pool belongs to.') +param devCenterProjectResourceId string + +@description('Optional. The subnet id on which to put all machines created in the pool.') +param subnetResourceId string? + +@description('Required. Defines how the machine will be handled once it executed a job.') +param agentProfile agentProfileType + +@description('Optional. The OS profile of the agents in the pool.') +param osProfile osProfileType = { + logonType: 'Interactive' + secretsManagementSettings: { + keyExportable: false + observedCertificates: [] + } +} + +@description('Optional. The storage profile of the machines in the pool.') +param storageProfile storageProfileType + +@description('Required. Defines the organization in which the pool will be used.') +param organizationProfile organizationProfileType + +@description('Optional. Tags of the resource.') +param tags object? + +@description('Optional. The lock settings of the service.') +param lock lockType + +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Optional. The diagnostic settings of the service.') +param diagnosticSettings diagnosticSettingType + +@description('Optional. The managed service identities assigned to this resource.') +@metadata({ + example: ''' + { + systemAssigned: true, + userAssignedResourceIds: [ + '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity' + ] + } + { + systemAssigned: true + } + ''' +}) +param managedIdentities managedIdentitiesType + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) +} + +var formattedRoleAssignments = [ + for (roleAssignment, index) in (roleAssignments ?? []): union(roleAssignment, { + roleDefinitionId: builtInRoleNames[?roleAssignment.roleDefinitionIdOrName] ?? (contains( + roleAssignment.roleDefinitionIdOrName, + '/providers/Microsoft.Authorization/roleDefinitions/' + ) + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName)) + }) +] + +var formattedUserAssignedIdentities = reduce( + map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), + {}, + (cur, next) => union(cur, next) +) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} } + +var identity = !empty(managedIdentities) + ? { + type: (managedIdentities.?systemAssigned ?? false) + ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned') + : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : 'None') + userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null + } + : null + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { + name: '46d3xbcp.res.devopsinfrastructure-pool.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } +} + +resource managedDevOpsPool 'Microsoft.DevOpsInfrastructure/pools@2024-04-04-preview' = { + name: name + location: location + tags: tags + identity: identity + properties: { + agentProfile: agentProfile + devCenterProjectResourceId: devCenterProjectResourceId + fabricProfile: { + sku: { + name: fabricProfileSkuName + } + networkProfile: !empty(subnetResourceId) + ? { + subnetId: subnetResourceId! + } + : null + osProfile: osProfile + storageProfile: storageProfile + kind: 'Vmss' + images: images + } + maximumConcurrency: concurrency + organizationProfile: organizationProfile + } +} + +resource managedDevOpsPool_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' + ? 'Cannot delete resource or child resources.' + : 'Cannot delete or modify the resource or child resources.' + } + scope: managedDevOpsPool +} + +resource managedDevOpsPool_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + managedDevOpsPool.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) + properties: { + roleDefinitionId: roleAssignment.roleDefinitionId + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: managedDevOpsPool + } +] + +resource managedDevOpsPool_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ + for (diagnosticSetting, index) in (diagnosticSettings ?? []): { + name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' + properties: { + storageAccountId: diagnosticSetting.?storageAccountResourceId + workspaceId: diagnosticSetting.?workspaceResourceId + eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId + eventHubName: diagnosticSetting.?eventHubName + metrics: [ + for group in (diagnosticSetting.?metricCategories ?? [{ category: 'AllMetrics' }]): { + category: group.category + enabled: group.?enabled ?? true + timeGrain: null + } + ] + logs: [ + for group in (diagnosticSetting.?logCategoriesAndGroups ?? [{ categoryGroup: 'allLogs' }]): { + categoryGroup: group.?categoryGroup + category: group.?category + enabled: group.?enabled ?? true + } + ] + marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId + logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType + } + scope: managedDevOpsPool + } +] + +@description('The name of the Managed DevOps Pool.') +output name string = managedDevOpsPool.name + +@description('The resource ID of the Managed DevOps Pool.') +output resourceId string = managedDevOpsPool.id + +@description('The name of the resource group the Managed DevOps Pool resource was deployed into.') +output resourceGroupName string = resourceGroup().name + +@description('The location the Managed DevOps Pool resource was deployed into.') +output location string = managedDevOpsPool.location + +@description('The principal ID of the system assigned identity.') +output systemAssignedMIPrincipalId string? = managedDevOpsPool.?identity.?principalId + +type osProfileType = { + @description('Required. The logon type of the machine.') + logonType: ('Interactive' | 'Service') + + @description('Optional. The secret management settings of the machines in the pool.') + secretsManagementSettings: { + @description('Required. The secret management settings of the machines in the pool.') + keyExportable: bool + + @description('Required. The list of certificates to install on all machines in the pool.') + observedCertificates: string[] + + @description('Optional. Where to store certificates on the machine.') + certificateStoreLocation: string? + }? +} + +type storageProfileType = { + @description('Optional. The Azure SKU name of the machines in the pool.') + osDiskStorageAccountType: ('Premium' | 'StandardSSD' | 'Standard')? + + @description('Optional. A list of empty data disks to attach.') + dataDisks: { + @description('Optional. The type of caching to be enabled for the data disks. The default value for caching is readwrite. For information about the caching options see: https://blogs.msdn.microsoft.com/windowsazurestorage/2012/06/27/exploring-windows-azure-drives-disks-and-images/.') + caching: ('None' | 'ReadOnly' | 'ReadWrite')? + + @description('Optional. The initial disk size in gigabytes.') + diskSizeGiB: int? + + @description('Optional. The drive letter for the empty data disk. If not specified, it will be the first available letter. Letters A, C, D, and E are not allowed.') + driveLetter: string? + + @description('Optional. The storage Account type to be used for the data disk. If omitted, the default is Standard_LRS.') + storageAccountType: ('Premium_LRS' | 'Premium_ZRS' | 'StandardSSD_LRS' | 'StandardSSD_ZRS' | 'Standard_LRS')? + }[]? +}? + +type imageType = { + @description('Optional. List of aliases to reference the image by.') + aliases: string[]? + + @description('Optional. The percentage of the buffer to be allocated to this image.') + buffer: string? + + @description('Required. The image to use from a well-known set of images made available to customers.') + wellKnownImageName: string + + @description('Optional. The resource id of the image.') + resourceId: string? +}[] + +type organizationProfileType = { + @description('Required. Azure DevOps organization profile.') + kind: 'AzureDevOps' + + @description('Optional. The type of permission which determines which accounts are admins on the Azure DevOps pool.') + permissionProfile: { + @description('Optional. Determines who has admin permissions to the Azure DevOps pool.') + kind: ('CreatorOnly' | 'Inherit' | 'SpecificAccounts')? + + @description('Optional. Group email addresses.') + groups: string[]? + + @description('Optional. User email addresses.') + users: string[]? + }? + + @description('Required. The list of Azure DevOps organizations the pool should be present in..') + organizations: { + @description('Required. The Azure DevOps organization URL in which the pool should be created.') + url: string + + @description('Optional. List of projects in which the pool should be created.') + projects: string[]? + + @description('Optional. How many machines can be created at maximum in this organization out of the maximumConcurrency of the pool.') + @minValue(1) + @maxValue(10000) + parallelism: int? + }[] +} + +type dataDiskType = { + @description('Optional. The type of caching to be enabled for the data disks. The default value for caching is readwrite. For information about the caching options see: https://blogs.msdn.microsoft.com/windowsazurestorage/2012/06/27/exploring-windows-azure-drives-disks-and-images/.') + caching: ('None' | 'ReadOnly' | 'ReadWrite')? + + @description('Optional. The initial disk size in gigabytes.') + diskSizeGiB: int? + + @description('Optional. The drive letter for the empty data disk. If not specified, it will be the first available letter. Letters A, C, D, and E are not allowed.') + driveLetter: string? + + @description('Optional. The storage Account type to be used for the data disk. If omitted, the default is Standard_LRS.') + storageAccountType: ('Premium_LRS' | 'Premium_ZRS' | 'StandardSSD_LRS' | 'StandardSSD_ZRS' | 'Standard_LRS')? +}[]? + +type resourcePredictionsProfileAutomaticType = { + @description('Required. The stand-by agent scheme is determined based on historical demand.') + kind: 'Automatic' + + @description('Required. Determines the balance between cost and performance.') + predictionPreference: 'Balanced' | 'MostCostEffective' | 'MoreCostEffective' | 'MorePerformance' | 'BestPerformance' +} + +type resourcePredictionsProfileManualType = { + @description('Required. Customer provides the stand-by agent scheme.') + kind: 'Manual' +} + +type agentStatefulType = { + @description('Required. Stateful profile meaning that the machines will be returned to the pool after running a job.') + kind: 'Stateful' + + @description('Required. How long should stateful machines be kept around. The maximum is one week.') + maxAgentLifetime: string + + @description('Required. How long should the machine be kept around after it ran a workload when there are no stand-by agents. The maximum is one week.') + gracePeriodTimeSpan: string + + @description('Optional. Defines pool buffer/stand-by agents.') + resourcePredictions: object? + + @discriminator('kind') + @description('Optional. Determines how the stand-by scheme should be provided.') + resourcePredictionsProfile: (resourcePredictionsProfileAutomaticType | resourcePredictionsProfileManualType)? +} + +type agentStatelessType = { + @description('Required. Stateless profile meaning that the machines will be cleaned up after running a job.') + kind: 'Stateless' + + @description('Optional. Defines pool buffer/stand-by agents.') + resourcePredictions: { + @description('Required. The time zone in which the daysData is provided. To see the list of available time zones, see: https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/default-time-zones?view=windows-11#time-zones or via PowerShell command `(Get-TimeZone -ListAvailable).StandardName`.') + timeZone: string + + @description('Optional. The number of agents needed at a specific time.') + @metadata({ + example: ''' + [ + { // Monday + '09:00': 5 + '22:00': 0 + } + {} // Tuesday + {} // Wednesday + {} // Thursday + { // Friday + '09:00': 5 + '22:00': 0 + } + {} // Saturday + {} // Sunday + ] + ''' + }) + daysData: object[]? + }? + + @discriminator('kind') + @description('Optional. Determines how the stand-by scheme should be provided.') + resourcePredictionsProfile: (resourcePredictionsProfileAutomaticType | resourcePredictionsProfileManualType)? +} + +@discriminator('kind') +type agentProfileType = agentStatefulType | agentStatelessType + +type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') + roleDefinitionIdOrName: string + + @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') + principalId: string + + @description('Optional. The principal type of the assigned principal ID.') + principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')? + + @description('Optional. The description of the role assignment.') + description: string? + + @description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') + condition: string? + + @description('Optional. Version of the condition.') + conditionVersion: '2.0'? + + @description('Optional. The Resource Id of the delegated managed identity resource.') + delegatedManagedIdentityResourceId: string? +}[]? + +type lockType = { + @description('Optional. Specify the name of lock.') + name: string? + + @description('Optional. Specify the type of lock.') + kind: ('CanNotDelete' | 'ReadOnly' | 'None')? +}? + +type diagnosticSettingType = { + @description('Optional. The name of diagnostic setting.') + name: string? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection.') + logCategoriesAndGroups: { + @description('Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.') + category: string? + + @description('Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs.') + categoryGroup: string? + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @description('Optional. The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection.') + metricCategories: { + @description('Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics.') + category: string + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @description('Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type.') + logAnalyticsDestinationType: ('Dedicated' | 'AzureDiagnostics')? + + @description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + workspaceResourceId: string? + + @description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + storageAccountResourceId: string? + + @description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') + eventHubAuthorizationRuleResourceId: string? + + @description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + eventHubName: string? + + @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') + marketplacePartnerResourceId: string? +}[]? + +type managedIdentitiesType = { + @description('Optional. Enables system assigned managed identity on the resource.') + systemAssigned: bool? + + @description('Optional. The resource ID(s) to assign to the resource.') + userAssignedResourceIds: string[]? +}? diff --git a/avm/res/dev-ops-infrastructure/pool/main.json b/avm/res/dev-ops-infrastructure/pool/main.json new file mode 100644 index 0000000000..38edaf771a --- /dev/null +++ b/avm/res/dev-ops-infrastructure/pool/main.json @@ -0,0 +1,1013 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "9138966756251042726" + }, + "name": "Managed DevOps Pool", + "description": "This module deploys the Managed DevOps Pool resource.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "osProfileType": { + "type": "object", + "properties": { + "logonType": { + "type": "string", + "allowedValues": [ + "Interactive", + "Service" + ], + "metadata": { + "description": "Required. The logon type of the machine." + } + }, + "secretsManagementSettings": { + "type": "object", + "properties": { + "keyExportable": { + "type": "bool", + "metadata": { + "description": "Required. The secret management settings of the machines in the pool." + } + }, + "observedCertificates": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The list of certificates to install on all machines in the pool." + } + }, + "certificateStoreLocation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Where to store certificates on the machine." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The secret management settings of the machines in the pool." + } + } + } + }, + "storageProfileType": { + "type": "object", + "properties": { + "osDiskStorageAccountType": { + "type": "string", + "allowedValues": [ + "Premium", + "Standard", + "StandardSSD" + ], + "nullable": true, + "metadata": { + "description": "Optional. The Azure SKU name of the machines in the pool." + } + }, + "dataDisks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "caching": { + "type": "string", + "allowedValues": [ + "None", + "ReadOnly", + "ReadWrite" + ], + "nullable": true, + "metadata": { + "description": "Optional. The type of caching to be enabled for the data disks. The default value for caching is readwrite. For information about the caching options see: https://blogs.msdn.microsoft.com/windowsazurestorage/2012/06/27/exploring-windows-azure-drives-disks-and-images/." + } + }, + "diskSizeGiB": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The initial disk size in gigabytes." + } + }, + "driveLetter": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The drive letter for the empty data disk. If not specified, it will be the first available letter. Letters A, C, D, and E are not allowed." + } + }, + "storageAccountType": { + "type": "string", + "allowedValues": [ + "Premium_LRS", + "Premium_ZRS", + "StandardSSD_LRS", + "StandardSSD_ZRS", + "Standard_LRS" + ], + "nullable": true, + "metadata": { + "description": "Optional. The storage Account type to be used for the data disk. If omitted, the default is Standard_LRS." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of empty data disks to attach." + } + } + }, + "nullable": true + }, + "imageType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "aliases": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of aliases to reference the image by." + } + }, + "buffer": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The percentage of the buffer to be allocated to this image." + } + }, + "wellKnownImageName": { + "type": "string", + "metadata": { + "description": "Required. The image to use from a well-known set of images made available to customers." + } + }, + "resourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource id of the image." + } + } + } + } + }, + "organizationProfileType": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "allowedValues": [ + "AzureDevOps" + ], + "metadata": { + "description": "Required. Azure DevOps organization profile." + } + }, + "permissionProfile": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "allowedValues": [ + "CreatorOnly", + "Inherit", + "SpecificAccounts" + ], + "nullable": true, + "metadata": { + "description": "Optional. Determines who has admin permissions to the Azure DevOps pool." + } + }, + "groups": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Group email addresses." + } + }, + "users": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. User email addresses." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The type of permission which determines which accounts are admins on the Azure DevOps pool." + } + }, + "organizations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "url": { + "type": "string", + "metadata": { + "description": "Required. The Azure DevOps organization URL in which the pool should be created." + } + }, + "projects": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. List of projects in which the pool should be created." + } + }, + "parallelism": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 10000, + "metadata": { + "description": "Optional. How many machines can be created at maximum in this organization out of the maximumConcurrency of the pool." + } + } + } + }, + "metadata": { + "description": "Required. The list of Azure DevOps organizations the pool should be present in.." + } + } + } + }, + "dataDiskType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "caching": { + "type": "string", + "allowedValues": [ + "None", + "ReadOnly", + "ReadWrite" + ], + "nullable": true, + "metadata": { + "description": "Optional. The type of caching to be enabled for the data disks. The default value for caching is readwrite. For information about the caching options see: https://blogs.msdn.microsoft.com/windowsazurestorage/2012/06/27/exploring-windows-azure-drives-disks-and-images/." + } + }, + "diskSizeGiB": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. The initial disk size in gigabytes." + } + }, + "driveLetter": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The drive letter for the empty data disk. If not specified, it will be the first available letter. Letters A, C, D, and E are not allowed." + } + }, + "storageAccountType": { + "type": "string", + "allowedValues": [ + "Premium_LRS", + "Premium_ZRS", + "StandardSSD_LRS", + "StandardSSD_ZRS", + "Standard_LRS" + ], + "nullable": true, + "metadata": { + "description": "Optional. The storage Account type to be used for the data disk. If omitted, the default is Standard_LRS." + } + } + } + }, + "nullable": true + }, + "resourcePredictionsProfileAutomaticType": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "allowedValues": [ + "Automatic" + ], + "metadata": { + "description": "Required. The stand-by agent scheme is determined based on historical demand." + } + }, + "predictionPreference": { + "type": "string", + "allowedValues": [ + "Balanced", + "BestPerformance", + "MoreCostEffective", + "MorePerformance", + "MostCostEffective" + ], + "metadata": { + "description": "Required. Determines the balance between cost and performance." + } + } + } + }, + "resourcePredictionsProfileManualType": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "allowedValues": [ + "Manual" + ], + "metadata": { + "description": "Required. Customer provides the stand-by agent scheme." + } + } + } + }, + "agentStatefulType": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "allowedValues": [ + "Stateful" + ], + "metadata": { + "description": "Required. Stateful profile meaning that the machines will be returned to the pool after running a job." + } + }, + "maxAgentLifetime": { + "type": "string", + "metadata": { + "description": "Required. How long should stateful machines be kept around. The maximum is one week." + } + }, + "gracePeriodTimeSpan": { + "type": "string", + "metadata": { + "description": "Required. How long should the machine be kept around after it ran a workload when there are no stand-by agents. The maximum is one week." + } + }, + "resourcePredictions": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Defines pool buffer/stand-by agents." + } + }, + "resourcePredictionsProfile": { + "type": "object", + "discriminator": { + "propertyName": "kind", + "mapping": { + "Automatic": { + "$ref": "#/definitions/resourcePredictionsProfileAutomaticType" + }, + "Manual": { + "$ref": "#/definitions/resourcePredictionsProfileManualType" + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Determines how the stand-by scheme should be provided." + } + } + } + }, + "agentStatelessType": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "allowedValues": [ + "Stateless" + ], + "metadata": { + "description": "Required. Stateless profile meaning that the machines will be cleaned up after running a job." + } + }, + "resourcePredictions": { + "type": "object", + "properties": { + "timeZone": { + "type": "string", + "metadata": { + "description": "Required. The time zone in which the daysData is provided. To see the list of available time zones, see: https://learn.microsoft.com/en-us/windows-hardware/manufacture/desktop/default-time-zones?view=windows-11#time-zones or via PowerShell command `(Get-TimeZone -ListAvailable).StandardName`." + } + }, + "daysData": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "example": " [\n { // Monday\n '09:00': 5\n '22:00': 0\n }\n {} // Tuesday\n {} // Wednesday\n {} // Thursday\n { // Friday\n '09:00': 5\n '22:00': 0\n }\n {} // Saturday\n {} // Sunday\n ]\n ", + "description": "Optional. The number of agents needed at a specific time." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Defines pool buffer/stand-by agents." + } + }, + "resourcePredictionsProfile": { + "type": "object", + "discriminator": { + "propertyName": "kind", + "mapping": { + "Automatic": { + "$ref": "#/definitions/resourcePredictionsProfileAutomaticType" + }, + "Manual": { + "$ref": "#/definitions/resourcePredictionsProfileManualType" + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Determines how the stand-by scheme should be provided." + } + } + } + }, + "agentProfileType": { + "type": "object", + "discriminator": { + "propertyName": "kind", + "mapping": { + "Stateful": { + "$ref": "#/definitions/agentStatefulType" + }, + "Stateless": { + "$ref": "#/definitions/agentStatelessType" + } + } + } + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + }, + "managedIdentitiesType": { + "type": "object", + "properties": { + "systemAssigned": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enables system assigned managed identity on the resource." + } + }, + "userAssignedResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The resource ID(s) to assign to the resource." + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the pool. It needs to be globally unique." + } + }, + "fabricProfileSkuName": { + "type": "string", + "metadata": { + "description": "Required. The Azure SKU name of the machines in the pool." + } + }, + "concurrency": { + "type": "int", + "minValue": 1, + "maxValue": 10000, + "metadata": { + "description": "Required. Defines how many resources can there be created at any given time." + } + }, + "images": { + "$ref": "#/definitions/imageType", + "metadata": { + "description": "Required. The VM images of the machines in the pool." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. The geo-location where the resource lives." + } + }, + "devCenterProjectResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the DevCenter Project the pool belongs to." + } + }, + "subnetResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subnet id on which to put all machines created in the pool." + } + }, + "agentProfile": { + "$ref": "#/definitions/agentProfileType", + "metadata": { + "description": "Required. Defines how the machine will be handled once it executed a job." + } + }, + "osProfile": { + "$ref": "#/definitions/osProfileType", + "defaultValue": { + "logonType": "Interactive", + "secretsManagementSettings": { + "keyExportable": false, + "observedCertificates": [] + } + }, + "metadata": { + "description": "Optional. The OS profile of the agents in the pool." + } + }, + "storageProfile": { + "$ref": "#/definitions/storageProfileType", + "metadata": { + "description": "Optional. The storage profile of the machines in the pool." + } + }, + "organizationProfile": { + "$ref": "#/definitions/organizationProfileType", + "metadata": { + "description": "Required. Defines the organization in which the pool will be used." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "managedIdentities": { + "$ref": "#/definitions/managedIdentitiesType", + "metadata": { + "example": " {\n systemAssigned: true,\n userAssignedResourceIds: [\n '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity'\n ]\n }\n {\n systemAssigned: true\n }\n ", + "description": "Optional. The managed service identities assigned to this resource." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + }, + "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]" + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.devopsinfrastructure-pool.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "managedDevOpsPool": { + "type": "Microsoft.DevOpsInfrastructure/pools", + "apiVersion": "2024-04-04-preview", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "identity": "[variables('identity')]", + "properties": { + "agentProfile": "[parameters('agentProfile')]", + "devCenterProjectResourceId": "[parameters('devCenterProjectResourceId')]", + "fabricProfile": { + "sku": { + "name": "[parameters('fabricProfileSkuName')]" + }, + "networkProfile": "[if(not(empty(parameters('subnetResourceId'))), createObject('subnetId', parameters('subnetResourceId')), null())]", + "osProfile": "[parameters('osProfile')]", + "storageProfile": "[parameters('storageProfile')]", + "kind": "Vmss", + "images": "[parameters('images')]" + }, + "maximumConcurrency": "[parameters('concurrency')]", + "organizationProfile": "[parameters('organizationProfile')]" + } + }, + "managedDevOpsPool_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.DevOpsInfrastructure/pools/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "managedDevOpsPool" + ] + }, + "managedDevOpsPool_roleAssignments": { + "copy": { + "name": "managedDevOpsPool_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.DevOpsInfrastructure/pools/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.DevOpsInfrastructure/pools', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "managedDevOpsPool" + ] + }, + "managedDevOpsPool_diagnosticSettings": { + "copy": { + "name": "managedDevOpsPool_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.DevOpsInfrastructure/pools/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "managedDevOpsPool" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the Managed DevOps Pool." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Managed DevOps Pool." + }, + "value": "[resourceId('Microsoft.DevOpsInfrastructure/pools', parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the Managed DevOps Pool resource was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the Managed DevOps Pool resource was deployed into." + }, + "value": "[reference('managedDevOpsPool', '2024-04-04-preview', 'full').location]" + }, + "systemAssignedMIPrincipalId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "The principal ID of the system assigned identity." + }, + "value": "[tryGet(tryGet(reference('managedDevOpsPool', '2024-04-04-preview', 'full'), 'identity'), 'principalId')]" + } + } +} \ No newline at end of file diff --git a/avm/res/dev-ops-infrastructure/pool/tests/e2e/defaults/dependencies.bicep b/avm/res/dev-ops-infrastructure/pool/tests/e2e/defaults/dependencies.bicep new file mode 100644 index 0000000000..367ae50af8 --- /dev/null +++ b/avm/res/dev-ops-infrastructure/pool/tests/e2e/defaults/dependencies.bicep @@ -0,0 +1,27 @@ +@description('Required. The name of the Dev Center.') +param devCenterName string + +@description('Required. The name of the Dev Center Project.') +param devCenterProjectName string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +resource devCenter 'Microsoft.DevCenter/devcenters@2024-02-01' = { + name: devCenterName + location: location +} + +resource devCenterProject 'Microsoft.DevCenter/projects@2024-02-01' = { + name: devCenterProjectName + location: location + properties: { + devCenterId: devCenter.id + } +} + +@description('The resource ID of the created DevCenter.') +output devCenterResourceId string = devCenter.id + +@description('The resource ID of the created DevCenter Project.') +output devCenterProjectResourceId string = devCenterProject.id diff --git a/avm/res/dev-ops-infrastructure/pool/tests/e2e/defaults/main.test.bicep b/avm/res/dev-ops-infrastructure/pool/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..c21a95f505 --- /dev/null +++ b/avm/res/dev-ops-infrastructure/pool/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,78 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-dev-ops-infrastructure.pool-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'mdpmin' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Required. Name of the Azure DevOps Organization. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-AzureDevOpsOrganizationName\'.') +@secure() +param azureDevOpsOrganizationName string = '' + +// ============ // +// Dependencies // +// ============ // +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + devCenterName: 'dep-${namePrefix}-dc-${serviceShort}' + devCenterProjectName: 'dep-${namePrefix}-dcp-${serviceShort}' + } +} + +// ================= // +// General resources // +// ================= // +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + agentProfile: { + kind: 'Stateless' + } + concurrency: 1 + devCenterProjectResourceId: nestedDependencies.outputs.devCenterProjectResourceId + images: [ + { + wellKnownImageName: 'windows-2022/latest' + } + ] + fabricProfileSkuName: 'Standard_DS2_v2' + organizationProfile: { + kind: 'AzureDevOps' + organizations: [ + { + url: 'https://dev.azure.com/${azureDevOpsOrganizationName}' + } + ] + } + } + } +] diff --git a/avm/res/dev-ops-infrastructure/pool/tests/e2e/max/dependencies.bicep b/avm/res/dev-ops-infrastructure/pool/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..11333fc560 --- /dev/null +++ b/avm/res/dev-ops-infrastructure/pool/tests/e2e/max/dependencies.bicep @@ -0,0 +1,89 @@ +@description('Required. The name of the Dev Center.') +param devCenterName string + +@description('Required. The name of the Dev Center Project.') +param devCenterProjectName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the virtual network to create.') +param virtualNetworkName string + +@description('Required. The object ID of the Entra ID-provided DevOpsInfrastructure principal.') +param devOpsInfrastructureObjectID string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +var addressPrefix = '192.168.1.0' + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource devCenter 'Microsoft.DevCenter/devcenters@2024-02-01' = { + name: devCenterName + location: location +} + +resource devCenterProject 'Microsoft.DevCenter/projects@2024-02-01' = { + name: devCenterProjectName + location: location + properties: { + devCenterId: devCenter.id + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2021-02-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + cidrSubnet(addressPrefix, 24, 0) + ] + } + subnets: [ + { + name: 'default' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 0) + delegations: [ + { + name: 'Microsoft.DevOpsInfrastructure/pools' + properties: { + serviceName: 'Microsoft.DevOpsInfrastructure/pools' + } + } + ] + } + } + ] + } +} + +// Network Contributor role assignment +resource roleAssignments 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { + name: guid(subscription().subscriptionId, 'DevOpsInfrastructure', 'Network Contributor', 'max') + properties: { + principalId: devOpsInfrastructureObjectID // DevOpsInfrastructure service principal + #disable-next-line use-resource-id-functions + roleDefinitionId: '/providers/Microsoft.Authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7' + principalType: 'ServicePrincipal' + } + scope: virtualNetwork +} + +@description('The resource ID of the created DevCenter.') +output devCenterResourceId string = devCenter.id + +@description('The resource ID of the created DevCenter Project.') +output devCenterProjectResourceId string = devCenterProject.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created subnet.') +output subnetResourceId string = first(virtualNetwork.properties.subnets)!.id diff --git a/avm/res/dev-ops-infrastructure/pool/tests/e2e/max/main.test.bicep b/avm/res/dev-ops-infrastructure/pool/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..7416f2934c --- /dev/null +++ b/avm/res/dev-ops-infrastructure/pool/tests/e2e/max/main.test.bicep @@ -0,0 +1,171 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set' +metadata description = 'This instance deploys the module with most of its features enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-dev-ops-infrastructure.pool-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'mdpmax' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Required. Name of the Azure DevOps Organization. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-AzureDevOpsOrganizationName\'.') +@secure() +param azureDevOpsOrganizationName string = '' + +@description('Required. Name of the Azure DevOps Max Project. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-AzureDevOpsProjectName\'.') +@secure() +param azureDevOpsProjectName string = '' + +@description('Required. The object ID of the Entra ID-provided DevOpsInfrastructure principal. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-DevOpsInfrastructureObjectID\'.') +@secure() +param devOpsInfrastructureObjectID string = '' + +// ============ // +// Dependencies // +// ============ // +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + devCenterName: 'dep-${namePrefix}-dc-${serviceShort}' + devCenterProjectName: 'dep-${namePrefix}-dcp-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + devOpsInfrastructureObjectID: devOpsInfrastructureObjectID + } +} + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + agentProfile: { + kind: 'Stateless' + resourcePredictions: { + timeZone: 'Central Europe Standard Time' + daysData: [ + // Monday + { + '09:00:00': 1 + '17:00:00': 0 + } + // Tuesday + {} + // Wednesday + {} + // Thursday + {} + // Friday + { + '09:00:00': 1 + '17:00:00': 0 + } + // Saturday + {} + // Sunday + {} + ] + } + resourcePredictionsProfile: { + kind: 'Automatic' + predictionPreference: 'Balanced' + } + } + concurrency: 1 + devCenterProjectResourceId: nestedDependencies.outputs.devCenterProjectResourceId + images: [ + { + aliases: [ + 'windows-2022' + ] + buffer: '*' + wellKnownImageName: 'windows-2022/latest' + } + ] + fabricProfileSkuName: 'Standard_D2_v2' + organizationProfile: { + kind: 'AzureDevOps' + organizations: [ + { + url: 'https://dev.azure.com/${azureDevOpsOrganizationName}' + parallelism: 1 + projects: [ + azureDevOpsProjectName + ] + } + ] + permissionProfile: { + kind: 'CreatorOnly' + } + } + storageProfile: { + osDiskStorageAccountType: 'Standard' + dataDisks: [ + { + caching: 'ReadWrite' + diskSizeGiB: 100 + driveLetter: 'B' + storageAccountType: 'Standard_LRS' + } + ] + } + subnetResourceId: nestedDependencies.outputs.subnetResourceId + roleAssignments: [ + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + } +] diff --git a/avm/res/dev-ops-infrastructure/pool/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/dev-ops-infrastructure/pool/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..0afb813bac --- /dev/null +++ b/avm/res/dev-ops-infrastructure/pool/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,89 @@ +@description('Required. The name of the Dev Center.') +param devCenterName string + +@description('Required. The name of the Dev Center Project.') +param devCenterProjectName string + +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + +@description('Required. The name of the virtual network to create.') +param virtualNetworkName string + +@description('Required. The object ID of the Entra ID-provided DevOpsInfrastructure principal.') +param devOpsInfrastructureObjectID string + +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +var addressPrefix = '192.168.1.0' + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource devCenter 'Microsoft.DevCenter/devcenters@2024-02-01' = { + name: devCenterName + location: location +} + +resource devCenterProject 'Microsoft.DevCenter/projects@2024-02-01' = { + name: devCenterProjectName + location: location + properties: { + devCenterId: devCenter.id + } +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2021-02-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + cidrSubnet(addressPrefix, 24, 0) + ] + } + subnets: [ + { + name: 'default' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 0) + delegations: [ + { + name: 'Microsoft.DevOpsInfrastructure/pools' + properties: { + serviceName: 'Microsoft.DevOpsInfrastructure/pools' + } + } + ] + } + } + ] + } +} + +// Network Contributor role assignment +resource roleAssignments 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { + name: guid(subscription().subscriptionId, 'DevOpsInfrastructure', 'Network Contributor', 'waf') + properties: { + principalId: devOpsInfrastructureObjectID // DevOpsInfrastructure service principal + #disable-next-line use-resource-id-functions + roleDefinitionId: '/providers/Microsoft.Authorization/roleDefinitions/4d97b98b-1d4f-4787-a291-c67834d212e7' + principalType: 'ServicePrincipal' + } + scope: virtualNetwork +} + +@description('The resource ID of the created DevCenter.') +output devCenterResourceId string = devCenter.id + +@description('The resource ID of the created DevCenter Project.') +output devCenterProjectResourceId string = devCenterProject.id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created subnet.') +output subnetResourceId string = first(virtualNetwork.properties.subnets)!.id diff --git a/avm/res/dev-ops-infrastructure/pool/tests/e2e/waf-aligned/main.test.bicep b/avm/res/dev-ops-infrastructure/pool/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..fdf015444f --- /dev/null +++ b/avm/res/dev-ops-infrastructure/pool/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,127 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-dev-ops-infrastructure.pool-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'mdpwaf' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Required. Name of the Azure DevOps Organization. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-AzureDevOpsOrganizationName\'.') +@secure() +param azureDevOpsOrganizationName string = '' + +@description('Required. Name of the Azure DevOps WAF Project. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-AzureDevOpsProjectName\'.') +@secure() +param azureDevOpsProjectName string = '' + +@description('Required. The object ID of the Entra ID-provided DevOpsInfrastructure principal. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-DevOpsInfrastructureObjectID\'.') +@secure() +param devOpsInfrastructureObjectID string = '' + +// ============ // +// Dependencies // +// ============ // +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + devCenterName: 'dep-${namePrefix}-dc-${serviceShort}' + devCenterProjectName: 'dep-${namePrefix}-dcp-${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + devOpsInfrastructureObjectID: devOpsInfrastructureObjectID + } +} + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + agentProfile: { + kind: 'Stateless' + resourcePredictions: { + timeZone: 'Central Europe Standard Time' + daysData: [ + // Monday + { + '09:00:00': 1 + '17:00:00': 0 + } + // Tuesday + {} + // Wednesday + {} + // Thursday + {} + // Friday + { + '09:00:00': 1 + '17:00:00': 0 + } + // Saturday + {} + // Sunday + {} + ] + } + resourcePredictionsProfile: { + kind: 'Automatic' + predictionPreference: 'Balanced' + } + } + concurrency: 1 + devCenterProjectResourceId: nestedDependencies.outputs.devCenterProjectResourceId + images: [ + { + wellKnownImageName: 'windows-2022/latest' + } + ] + fabricProfileSkuName: 'Standard_D2_v2' + subnetResourceId: nestedDependencies.outputs.subnetResourceId + organizationProfile: { + kind: 'AzureDevOps' + organizations: [ + { + url: 'https://dev.azure.com/${azureDevOpsOrganizationName}' + projects: [ + azureDevOpsProjectName + ] + parallelism: 1 + } + ] + permissionProfile: { + kind: 'CreatorOnly' + } + } + } + } +] diff --git a/avm/res/dev-ops-infrastructure/pool/version.json b/avm/res/dev-ops-infrastructure/pool/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/res/dev-ops-infrastructure/pool/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From 7e27b6c397704335dd03db6a9542432de0e6ee44 Mon Sep 17 00:00:00 2001 From: hundredacres Date: Thu, 19 Sep 2024 02:53:02 -0700 Subject: [PATCH 25/57] fix: Remove ORPHANED status from module and update resource API version - `avm/res/network/front-door` (#3320) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Remove ORPHANED file from module. Update resource API version. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.network.front-door](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.res.network.front-door.yml/badge.svg?branch=avm%2Fissues%2F1269)](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.res.network.front-door.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Máté Barabás Co-authored-by: Rainer Halanek <61878316+rahalan@users.noreply.github.com> Co-authored-by: JFolberth --- avm/res/network/front-door/ORPHANED.md | 4 ---- avm/res/network/front-door/README.md | 7 +------ avm/res/network/front-door/main.bicep | 2 +- avm/res/network/front-door/main.json | 4 ++-- 4 files changed, 4 insertions(+), 13 deletions(-) delete mode 100644 avm/res/network/front-door/ORPHANED.md diff --git a/avm/res/network/front-door/ORPHANED.md b/avm/res/network/front-door/ORPHANED.md deleted file mode 100644 index ef8fa911d2..0000000000 --- a/avm/res/network/front-door/ORPHANED.md +++ /dev/null @@ -1,4 +0,0 @@ -⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️ - -- Only security and bug fixes are being handled by the AVM core team at present. -- If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)! \ No newline at end of file diff --git a/avm/res/network/front-door/README.md b/avm/res/network/front-door/README.md index 50e01165f6..23561de663 100644 --- a/avm/res/network/front-door/README.md +++ b/avm/res/network/front-door/README.md @@ -1,10 +1,5 @@ # Azure Front Doors `[Microsoft.Network/frontDoors]` -> ⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️ -> -> - Only security and bug fixes are being handled by the AVM core team at present. -> - If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)! - This module deploys an Azure Front Door. ## Navigation @@ -22,7 +17,7 @@ This module deploys an Azure Front Door. | `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) | +| `Microsoft.Network/frontDoors` | [2021-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-06-01/frontDoors) | ## Usage examples diff --git a/avm/res/network/front-door/main.bicep b/avm/res/network/front-door/main.bicep index 6e47218f83..4ea7e31e76 100644 --- a/avm/res/network/front-door/main.bicep +++ b/avm/res/network/front-door/main.bicep @@ -101,7 +101,7 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } } -resource frontDoor 'Microsoft.Network/frontDoors@2020-05-01' = { +resource frontDoor 'Microsoft.Network/frontDoors@2021-06-01' = { name: name location: 'global' tags: tags diff --git a/avm/res/network/front-door/main.json b/avm/res/network/front-door/main.json index 9a41829948..6e1554fd90 100644 --- a/avm/res/network/front-door/main.json +++ b/avm/res/network/front-door/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "1188225161775362575" + "templateHash": "3109919695748867092" }, "name": "Azure Front Doors", "description": "This module deploys an Azure Front Door.", @@ -380,7 +380,7 @@ }, "frontDoor": { "type": "Microsoft.Network/frontDoors", - "apiVersion": "2020-05-01", + "apiVersion": "2021-06-01", "name": "[parameters('name')]", "location": "global", "tags": "[parameters('tags')]", From 62198b03cde2790b5e69d44b79d1d072f8a2456c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Fri, 20 Sep 2024 00:50:35 +0200 Subject: [PATCH 26/57] fix: `avm/ptn/deployment/import-image-to-acr` fixes bugs handling newImageName (#3329) ## Description Fixes bugs handling newImageName Closes #3326 ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.deployment-script.import-image-to-acr](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.ptn.deployment-script.import-image-to-acr.yml/badge.svg?branch=import-image-fixes)](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.ptn.deployment-script.import-image-to-acr.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [x] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/ptn/deployment-script/import-image-to-acr/README.md | 6 +++--- avm/ptn/deployment-script/import-image-to-acr/main.bicep | 6 ++++-- avm/ptn/deployment-script/import-image-to-acr/main.json | 6 +++--- .../import-image-to-acr/tests/e2e/max/main.test.bicep | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/avm/ptn/deployment-script/import-image-to-acr/README.md b/avm/ptn/deployment-script/import-image-to-acr/README.md index 42038de2c6..9015f8760e 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/README.md +++ b/avm/ptn/deployment-script/import-image-to-acr/README.md @@ -115,7 +115,7 @@ module importImageToAcr 'br/public:avm/ptn/deployment-script/import-image-to-acr cleanupPreference: 'OnExpiration' location: '' managedIdentities: '' - newImageName: 'your-image-name:tag' + newImageName: 'application/your-image-name:tag' overwriteExistingImage: true storageAccountResourceId: '' subnetResourceIds: '' @@ -163,7 +163,7 @@ module importImageToAcr 'br/public:avm/ptn/deployment-script/import-image-to-acr "value": "" }, "newImageName": { - "value": "your-image-name:tag" + "value": "application/your-image-name:tag" }, "overwriteExistingImage": { "value": true @@ -395,7 +395,7 @@ The new image name in the ACR. You can use this to import a publically available - Required: No - Type: string -- Default: `[last(split(parameters('image'), '/'))]` +- Default: `[string(skip(parameters('image'), add(indexOf(parameters('image'), '/'), 1)))]` - Example: `your-image-name:tag` ### Parameter: `overwriteExistingImage` diff --git a/avm/ptn/deployment-script/import-image-to-acr/main.bicep b/avm/ptn/deployment-script/import-image-to-acr/main.bicep index 64be560c36..61534011ce 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/main.bicep +++ b/avm/ptn/deployment-script/import-image-to-acr/main.bicep @@ -47,7 +47,7 @@ param sourceRegistryPassword string = '' @metadata({ example: 'your-image-name:tag' }) -param newImageName string = last(split(image, '/')) +param newImageName string = string(skip(image, indexOf(image, '/') + 1)) @description('Optional. The image will be overwritten if it already exists in the ACR with the same tag. Default is false.') param overwriteExistingImage bool = false @@ -234,7 +234,9 @@ output deploymentScriptOutput string[] = imageImport.outputs.deploymentScriptLog @description('An array of the imported images.') output importedImage importedImageType = { originalImage: image - acrHostedImage: '${acr.properties.loginServer}${string(skip(image, indexOf(image,'/')))}' + acrHostedImage: empty(newImageName) + ? '${acr.properties.loginServer}${string(skip(image, indexOf(image,'/')))}' + : '${acr.properties.loginServer}/${newImageName}' } // ================ // diff --git a/avm/ptn/deployment-script/import-image-to-acr/main.json b/avm/ptn/deployment-script/import-image-to-acr/main.json index 51751e27f8..4f8a4504ae 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/main.json +++ b/avm/ptn/deployment-script/import-image-to-acr/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "15179702678978782456" + "templateHash": "13166673091432959100" }, "name": "import-image-to-acr", "description": "This modules deployes an image to an Azure Container Registry.", @@ -127,7 +127,7 @@ }, "newImageName": { "type": "string", - "defaultValue": "[last(split(parameters('image'), '/'))]", + "defaultValue": "[string(skip(parameters('image'), add(indexOf(parameters('image'), '/'), 1)))]", "metadata": { "example": "your-image-name:tag", "description": "Optional. The new image name in the ACR. You can use this to import a publically available image with a custom name for later updating from e.g., your build pipeline." @@ -910,7 +910,7 @@ }, "value": { "originalImage": "[parameters('image')]", - "acrHostedImage": "[format('{0}{1}', reference('acr').loginServer, string(skip(parameters('image'), indexOf(parameters('image'), '/'))))]" + "acrHostedImage": "[if(empty(parameters('newImageName')), format('{0}{1}', reference('acr').loginServer, string(skip(parameters('image'), indexOf(parameters('image'), '/')))), format('{0}/{1}', reference('acr').loginServer, parameters('newImageName')))]" } } } diff --git a/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/main.test.bicep b/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/main.test.bicep index 3c7e75c684..8cd3fd3db1 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/main.test.bicep +++ b/avm/ptn/deployment-script/import-image-to-acr/tests/e2e/max/main.test.bicep @@ -69,7 +69,7 @@ module testDeployment '../../../main.bicep' = [ // commented out, as the user is not available in the test environment // sourceRegistryUsername: 'username' // sourceRegistryPassword: keyVault.getSecret(dependencies.outputs.keyVaultSecretName) - newImageName: 'your-image-name:tag' + newImageName: 'application/your-image-name:tag' cleanupPreference: 'OnExpiration' assignRbacRole: true managedIdentities: { userAssignedResourcesIds: [dependencies.outputs.managedIdentityResourceId] } From c447185e556f0586ae0e78c254886d9144978bd2 Mon Sep 17 00:00:00 2001 From: Fabio Masciotra Date: Fri, 20 Sep 2024 01:18:35 +0200 Subject: [PATCH 27/57] feat: experimental use of 'discriminator' for vpng-gw module (#2667) ## Description Closes https://github.com/Azure/bicep-registry-modules/issues/1869 Reviewed the code with the use of 'discriminator' for the UDT. Added the following test-cases: 1. BGP: VPN Active Passive with BGP config but no APIPA 2. BGPAA: VPN Active Active with BGP config but no APIPA 3. BGPAPIPA: VPN Active Passive with BGP config and APIPA 4. BGPAAAPIPA: VPN Active Active with BGP config and APIPA Plus other test-cases without BGP ## Pipeline Reference [![avm.res.network.virtual-network-gateway](https://github.com/fabmas/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network-gateway.yml/badge.svg?branch=vnetgw-draft)](https://github.com/fabmas/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network-gateway.yml) ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- .../network/virtual-network-gateway/README.md | 833 ++++++++++++++++-- .../virtual-network-gateway/main.bicep | 135 ++- .../network/virtual-network-gateway/main.json | 200 ++++- .../tests/e2e/aadvpn/main.test.bicep | 6 +- .../e2e/activeActiveBGP/dependencies.bicep | 49 ++ .../tests/e2e/activeActiveBGP/main.test.bicep | 83 ++ .../activeActiveBgpAPIPA/dependencies.bicep | 49 ++ .../e2e/activeActiveBgpAPIPA/main.test.bicep | 85 ++ .../e2e/activeActiveNoBGP/dependencies.bicep | 49 ++ .../e2e/activeActiveNoBGP/main.test.bicep | 83 ++ .../e2e/activePassiveBGP/dependencies.bicep | 49 ++ .../e2e/activePassiveBGP/main.test.bicep | 85 ++ .../e2e/activePassiveNoBGP/dependencies.bicep | 49 ++ .../e2e/activePassiveNoBGP/main.test.bicep | 83 ++ .../tests/e2e/defaults/main.test.bicep | 3 + .../tests/e2e/expressRoute/main.test.bicep | 3 + .../tests/e2e/max/main.test.bicep | 7 +- .../tests/e2e/vpn-no-az/main.test.bicep | 3 + .../tests/e2e/vpn/main.test.bicep | 4 +- .../tests/e2e/waf-aligned/main.test.bicep | 7 +- .../virtual-network-gateway/version.json | 2 +- 21 files changed, 1699 insertions(+), 168 deletions(-) create mode 100644 avm/res/network/virtual-network-gateway/tests/e2e/activeActiveBGP/dependencies.bicep create mode 100644 avm/res/network/virtual-network-gateway/tests/e2e/activeActiveBGP/main.test.bicep create mode 100644 avm/res/network/virtual-network-gateway/tests/e2e/activeActiveBgpAPIPA/dependencies.bicep create mode 100644 avm/res/network/virtual-network-gateway/tests/e2e/activeActiveBgpAPIPA/main.test.bicep create mode 100644 avm/res/network/virtual-network-gateway/tests/e2e/activeActiveNoBGP/dependencies.bicep create mode 100644 avm/res/network/virtual-network-gateway/tests/e2e/activeActiveNoBGP/main.test.bicep create mode 100644 avm/res/network/virtual-network-gateway/tests/e2e/activePassiveBGP/dependencies.bicep create mode 100644 avm/res/network/virtual-network-gateway/tests/e2e/activePassiveBGP/main.test.bicep create mode 100644 avm/res/network/virtual-network-gateway/tests/e2e/activePassiveNoBGP/dependencies.bicep create mode 100644 avm/res/network/virtual-network-gateway/tests/e2e/activePassiveNoBGP/main.test.bicep diff --git a/avm/res/network/virtual-network-gateway/README.md b/avm/res/network/virtual-network-gateway/README.md index 83cde965b0..644177396e 100644 --- a/avm/res/network/virtual-network-gateway/README.md +++ b/avm/res/network/virtual-network-gateway/README.md @@ -31,12 +31,17 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br/public:avm/res/network/virtual-network-gateway:`. - [AAD-VPN](#example-1-aad-vpn) -- [Using only defaults](#example-2-using-only-defaults) -- [ExpressRoute](#example-3-expressroute) -- [Using large parameter set](#example-4-using-large-parameter-set) -- [Using SKU without Availability Zones](#example-5-using-sku-without-availability-zones) -- [VPN](#example-6-vpn) -- [WAF-aligned](#example-7-waf-aligned) +- [VPN Active Active with BGP settings](#example-2-vpn-active-active-with-bgp-settings) +- [VPN Active Active with BGP settings](#example-3-vpn-active-active-with-bgp-settings) +- [VPN Active Active without BGP settings](#example-4-vpn-active-active-without-bgp-settings) +- [VPN Active Passive with BGP settings](#example-5-vpn-active-passive-with-bgp-settings) +- [VPN Active Passive without BGP settings](#example-6-vpn-active-passive-without-bgp-settings) +- [Using only defaults](#example-7-using-only-defaults) +- [ExpressRoute](#example-8-expressroute) +- [Using large parameter set](#example-9-using-large-parameter-set) +- [Using SKU without Availability Zones](#example-10-using-sku-without-availability-zones) +- [VPN](#example-11-vpn) +- [WAF-aligned](#example-12-waf-aligned) ### Example 1: _AAD-VPN_ @@ -52,32 +57,635 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: name: 'virtualNetworkGatewayDeployment' params: { // Required parameters + clusterSettings: { + clusterMode: 'activePassiveNoBgp' + } + gatewayType: 'Vpn' + name: 'nvgavpn001' + skuName: 'VpnGw2AZ' + vNetResourceId: '' + // Non-required parameters + domainNameLabel: [ + 'dm-nvgavpn' + ] + location: '' + publicIpZones: [ + 1 + 2 + 3 + ] + 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 + "clusterSettings": { + "value": { + "clusterMode": "activePassiveNoBgp" + } + }, + "gatewayType": { + "value": "Vpn" + }, + "name": { + "value": "nvgavpn001" + }, + "skuName": { + "value": "VpnGw2AZ" + }, + "vNetResourceId": { + "value": "" + }, + // Non-required parameters + "domainNameLabel": { + "value": [ + "dm-nvgavpn" + ] + }, + "location": { + "value": "" + }, + "publicIpZones": { + "value": [ + 1, + 2, + 3 + ] + }, + "vpnClientAadConfiguration": { + "value": { + "aadAudience": "41b23e61-6c1e-4545-b367-cd054e0ed4b4", + "aadIssuer": "", + "aadTenant": "", + "vpnAuthenticationTypes": [ + "AAD" + ], + "vpnClientProtocols": [ + "OpenVPN" + ] + } + }, + "vpnType": { + "value": "RouteBased" + } + } +} +``` + +
+

+ +### Example 2: _VPN Active Active with BGP settings_ + +This instance deploys the module with the VPN Active Active with BGP settings. + + +

+ +via Bicep module + +```bicep +module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway:' = { + name: 'virtualNetworkGatewayDeployment' + params: { + // Required parameters + clusterSettings: { + clusterMode: 'activeActiveBgp' + } + gatewayType: 'Vpn' + name: 'nvgaab001' + skuName: 'VpnGw2AZ' + vNetResourceId: '' + // Non-required parameters + allowRemoteVnetTraffic: true + disableIPSecReplayProtection: true + domainNameLabel: [ + 'dm-nvgaab' + ] + enableBgpRouteTranslationForNat: true + enablePrivateIpAddress: true + gatewayDefaultSiteLocalNetworkGatewayId: '' + location: '' + publicIpZones: [ + 1 + 2 + 3 + ] + 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 + "clusterSettings": { + "value": { + "clusterMode": "activeActiveBgp" + } + }, + "gatewayType": { + "value": "Vpn" + }, + "name": { + "value": "nvgaab001" + }, + "skuName": { + "value": "VpnGw2AZ" + }, + "vNetResourceId": { + "value": "" + }, + // Non-required parameters + "allowRemoteVnetTraffic": { + "value": true + }, + "disableIPSecReplayProtection": { + "value": true + }, + "domainNameLabel": { + "value": [ + "dm-nvgaab" + ] + }, + "enableBgpRouteTranslationForNat": { + "value": true + }, + "enablePrivateIpAddress": { + "value": true + }, + "gatewayDefaultSiteLocalNetworkGatewayId": { + "value": "" + }, + "location": { + "value": "" + }, + "publicIpZones": { + "value": [ + 1, + 2, + 3 + ] + }, + "vpnGatewayGeneration": { + "value": "Generation2" + }, + "vpnType": { + "value": "RouteBased" + } + } +} +``` + +
+

+ +### Example 3: _VPN Active Active with BGP settings_ + +This instance deploys the module with the VPN Active Active with APIPA BGP settings. + + +

+ +via Bicep module + +```bicep +module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway:' = { + name: 'virtualNetworkGatewayDeployment' + params: { + // Required parameters + clusterSettings: { + clusterMode: 'activeActiveBgp' + customBgpIpAddresses: [ + '169.254.21.4' + '169.254.21.5' + ] + secondCustomBgpIpAddresses: [ + '169.254.22.4' + '169.254.22.5' + ] + } + gatewayType: 'Vpn' + name: 'nvgaaa001' + skuName: 'VpnGw2AZ' + vNetResourceId: '' + // Non-required parameters + allowRemoteVnetTraffic: true + disableIPSecReplayProtection: true + domainNameLabel: [ + 'dm-nvgaaa' + ] + enableBgpRouteTranslationForNat: true + enablePrivateIpAddress: true + gatewayDefaultSiteLocalNetworkGatewayId: '' + location: '' + publicIpZones: [ + 1 + 2 + 3 + ] + 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 + "clusterSettings": { + "value": { + "clusterMode": "activeActiveBgp", + "customBgpIpAddresses": [ + "169.254.21.4", + "169.254.21.5" + ], + "secondCustomBgpIpAddresses": [ + "169.254.22.4", + "169.254.22.5" + ] + } + }, + "gatewayType": { + "value": "Vpn" + }, + "name": { + "value": "nvgaaa001" + }, + "skuName": { + "value": "VpnGw2AZ" + }, + "vNetResourceId": { + "value": "" + }, + // Non-required parameters + "allowRemoteVnetTraffic": { + "value": true + }, + "disableIPSecReplayProtection": { + "value": true + }, + "domainNameLabel": { + "value": [ + "dm-nvgaaa" + ] + }, + "enableBgpRouteTranslationForNat": { + "value": true + }, + "enablePrivateIpAddress": { + "value": true + }, + "gatewayDefaultSiteLocalNetworkGatewayId": { + "value": "" + }, + "location": { + "value": "" + }, + "publicIpZones": { + "value": [ + 1, + 2, + 3 + ] + }, + "vpnGatewayGeneration": { + "value": "Generation2" + }, + "vpnType": { + "value": "RouteBased" + } + } +} +``` + +
+

+ +### Example 4: _VPN Active Active without BGP settings_ + +This instance deploys the module with the VPN Active Active without BGP settings. + + +

+ +via Bicep module + +```bicep +module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway:' = { + name: 'virtualNetworkGatewayDeployment' + params: { + // Required parameters + clusterSettings: { + clusterMode: 'activeActiveNoBgp' + } + gatewayType: 'Vpn' + name: 'nvgaa001' + skuName: 'VpnGw2AZ' + vNetResourceId: '' + // Non-required parameters + allowRemoteVnetTraffic: true + disableIPSecReplayProtection: true + domainNameLabel: [ + 'dm-nvgaa' + ] + enableBgpRouteTranslationForNat: true + enablePrivateIpAddress: true + gatewayDefaultSiteLocalNetworkGatewayId: '' + location: '' + publicIpZones: [ + 1 + 2 + 3 + ] + 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 + "clusterSettings": { + "value": { + "clusterMode": "activeActiveNoBgp" + } + }, + "gatewayType": { + "value": "Vpn" + }, + "name": { + "value": "nvgaa001" + }, + "skuName": { + "value": "VpnGw2AZ" + }, + "vNetResourceId": { + "value": "" + }, + // Non-required parameters + "allowRemoteVnetTraffic": { + "value": true + }, + "disableIPSecReplayProtection": { + "value": true + }, + "domainNameLabel": { + "value": [ + "dm-nvgaa" + ] + }, + "enableBgpRouteTranslationForNat": { + "value": true + }, + "enablePrivateIpAddress": { + "value": true + }, + "gatewayDefaultSiteLocalNetworkGatewayId": { + "value": "" + }, + "location": { + "value": "" + }, + "publicIpZones": { + "value": [ + 1, + 2, + 3 + ] + }, + "vpnGatewayGeneration": { + "value": "Generation2" + }, + "vpnType": { + "value": "RouteBased" + } + } +} +``` + +
+

+ +### Example 5: _VPN Active Passive with BGP settings_ + +This instance deploys the module with the VPN Active Passive with APIPA BGP settings. + + +

+ +via Bicep module + +```bicep +module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway:' = { + name: 'virtualNetworkGatewayDeployment' + params: { + // Required parameters + clusterSettings: { + asn: 65815 + clusterMode: 'activePassiveBgp' + customBgpIpAddresses: [ + '169.254.21.4' + '169.254.21.5' + ] + } gatewayType: 'Vpn' - name: 'nvngavpn001' + name: 'nvgapb001' skuName: 'VpnGw2AZ' vNetResourceId: '' // Non-required parameters - activeActive: false + allowRemoteVnetTraffic: true + disableIPSecReplayProtection: true domainNameLabel: [ - 'dm-nvngavpn' + 'dm-nvgapb' ] + enableBgpRouteTranslationForNat: true + enablePrivateIpAddress: true + gatewayDefaultSiteLocalNetworkGatewayId: '' location: '' publicIpZones: [ 1 2 3 ] - vpnClientAadConfiguration: { - aadAudience: '41b23e61-6c1e-4545-b367-cd054e0ed4b4' - aadIssuer: '' - aadTenant: '' - vpnAuthenticationTypes: [ - 'AAD' + 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 + "clusterSettings": { + "value": { + "asn": 65815, + "clusterMode": "activePassiveBgp", + "customBgpIpAddresses": [ + "169.254.21.4", + "169.254.21.5" + ] + } + }, + "gatewayType": { + "value": "Vpn" + }, + "name": { + "value": "nvgapb001" + }, + "skuName": { + "value": "VpnGw2AZ" + }, + "vNetResourceId": { + "value": "" + }, + // Non-required parameters + "allowRemoteVnetTraffic": { + "value": true + }, + "disableIPSecReplayProtection": { + "value": true + }, + "domainNameLabel": { + "value": [ + "dm-nvgapb" ] - vpnClientProtocols: [ - 'OpenVPN' + }, + "enableBgpRouteTranslationForNat": { + "value": true + }, + "enablePrivateIpAddress": { + "value": true + }, + "gatewayDefaultSiteLocalNetworkGatewayId": { + "value": "" + }, + "location": { + "value": "" + }, + "publicIpZones": { + "value": [ + 1, + 2, + 3 ] + }, + "vpnGatewayGeneration": { + "value": "Generation2" + }, + "vpnType": { + "value": "RouteBased" + } + } +} +``` + +
+

+ +### Example 6: _VPN Active Passive without BGP settings_ + +This instance deploys the module with the VPN Active Passive without BGP settings. + + +

+ +via Bicep module + +```bicep +module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway:' = { + name: 'virtualNetworkGatewayDeployment' + params: { + // Required parameters + clusterSettings: { + clusterMode: 'activePassiveNoBgp' } + gatewayType: 'Vpn' + name: 'nvgap001' + skuName: 'VpnGw2AZ' + vNetResourceId: '' + // Non-required parameters + allowRemoteVnetTraffic: true + disableIPSecReplayProtection: true + domainNameLabel: [ + 'dm-nvgap' + ] + enableBgpRouteTranslationForNat: true + enablePrivateIpAddress: true + gatewayDefaultSiteLocalNetworkGatewayId: '' + location: '' + publicIpZones: [ + 1 + 2 + 3 + ] + vpnGatewayGeneration: 'Generation2' vpnType: 'RouteBased' } } @@ -96,11 +704,16 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "contentVersion": "1.0.0.0", "parameters": { // Required parameters + "clusterSettings": { + "value": { + "clusterMode": "activePassiveNoBgp" + } + }, "gatewayType": { "value": "Vpn" }, "name": { - "value": "nvngavpn001" + "value": "nvgap001" }, "skuName": { "value": "VpnGw2AZ" @@ -109,14 +722,26 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "value": "" }, // Non-required parameters - "activeActive": { - "value": false + "allowRemoteVnetTraffic": { + "value": true + }, + "disableIPSecReplayProtection": { + "value": true }, "domainNameLabel": { "value": [ - "dm-nvngavpn" + "dm-nvgap" ] }, + "enableBgpRouteTranslationForNat": { + "value": true + }, + "enablePrivateIpAddress": { + "value": true + }, + "gatewayDefaultSiteLocalNetworkGatewayId": { + "value": "" + }, "location": { "value": "" }, @@ -127,18 +752,8 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 3 ] }, - "vpnClientAadConfiguration": { - "value": { - "aadAudience": "41b23e61-6c1e-4545-b367-cd054e0ed4b4", - "aadIssuer": "", - "aadTenant": "", - "vpnAuthenticationTypes": [ - "AAD" - ], - "vpnClientProtocols": [ - "OpenVPN" - ] - } + "vpnGatewayGeneration": { + "value": "Generation2" }, "vpnType": { "value": "RouteBased" @@ -150,7 +765,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway:

-### Example 2: _Using only defaults_ +### Example 7: _Using only defaults_ This instance deploys the module with the minimum set of required parameters. @@ -164,6 +779,9 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: name: 'virtualNetworkGatewayDeployment' params: { // Required parameters + clusterSettings: { + clusterMode: 'activeActiveNoBgp' + } gatewayType: 'Vpn' name: 'nvgmin001' skuName: 'VpnGw2AZ' @@ -192,6 +810,11 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "contentVersion": "1.0.0.0", "parameters": { // Required parameters + "clusterSettings": { + "value": { + "clusterMode": "activeActiveNoBgp" + } + }, "gatewayType": { "value": "Vpn" }, @@ -222,7 +845,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway:

-### Example 3: _ExpressRoute_ +### Example 8: _ExpressRoute_ This instance deploys the module with the ExpressRoute set of required parameters. @@ -236,6 +859,9 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: name: 'virtualNetworkGatewayDeployment' params: { // Required parameters + clusterSettings: { + clusterMode: 'activePassiveBgp' + } gatewayType: 'ExpressRoute' name: 'nvger001' skuName: 'ErGw1AZ' @@ -268,6 +894,11 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "contentVersion": "1.0.0.0", "parameters": { // Required parameters + "clusterSettings": { + "value": { + "clusterMode": "activePassiveBgp" + } + }, "gatewayType": { "value": "ExpressRoute" }, @@ -306,7 +937,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway:

-### Example 4: _Using large parameter set_ +### Example 9: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -320,12 +951,23 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: name: 'virtualNetworkGatewayDeployment' params: { // Required parameters + clusterSettings: { + activeGatewayPipName: 'nvgmax001-pip2' + clusterMode: 'activeActiveBgp' + customBgpIpAddresses: [ + '169.254.21.4' + '169.254.21.5' + ] + secondCustomBgpIpAddresses: [ + '169.254.22.4' + '169.254.22.5' + ] + } gatewayType: 'Vpn' name: 'nvgmax001' skuName: 'VpnGw2AZ' vNetResourceId: '' // Non-required parameters - activeActive: true allowRemoteVnetTraffic: true diagnosticSettings: [ { @@ -435,6 +1077,20 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "contentVersion": "1.0.0.0", "parameters": { // Required parameters + "clusterSettings": { + "value": { + "activeGatewayPipName": "nvgmax001-pip2", + "clusterMode": "activeActiveBgp", + "customBgpIpAddresses": [ + "169.254.21.4", + "169.254.21.5" + ], + "secondCustomBgpIpAddresses": [ + "169.254.22.4", + "169.254.22.5" + ] + } + }, "gatewayType": { "value": "Vpn" }, @@ -448,9 +1104,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "value": "" }, // Non-required parameters - "activeActive": { - "value": true - }, "allowRemoteVnetTraffic": { "value": true }, @@ -580,7 +1233,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway:

-### Example 5: _Using SKU without Availability Zones_ +### Example 10: _Using SKU without Availability Zones_ This instance deploys the module with a SKU that does not support Availability Zones. @@ -594,6 +1247,9 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: name: 'virtualNetworkGatewayDeployment' params: { // Required parameters + clusterSettings: { + clusterMode: 'activePassiveNoBgp' + } gatewayType: 'Vpn' name: 'nvgnaz001' skuName: 'VpnGw1' @@ -617,6 +1273,11 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "contentVersion": "1.0.0.0", "parameters": { // Required parameters + "clusterSettings": { + "value": { + "clusterMode": "activePassiveNoBgp" + } + }, "gatewayType": { "value": "Vpn" }, @@ -640,7 +1301,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway:

-### Example 6: _VPN_ +### Example 11: _VPN_ This instance deploys the module with the VPN set of required parameters. @@ -654,12 +1315,14 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: name: 'virtualNetworkGatewayDeployment' params: { // Required parameters + clusterSettings: { + clusterMode: 'activeActiveNoBgp' + } gatewayType: 'Vpn' name: 'nvgvpn001' skuName: 'VpnGw2AZ' vNetResourceId: '' // Non-required parameters - activeActive: true allowRemoteVnetTraffic: true disableIPSecReplayProtection: true domainNameLabel: [ @@ -693,6 +1356,11 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "contentVersion": "1.0.0.0", "parameters": { // Required parameters + "clusterSettings": { + "value": { + "clusterMode": "activeActiveNoBgp" + } + }, "gatewayType": { "value": "Vpn" }, @@ -706,9 +1374,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "value": "" }, // Non-required parameters - "activeActive": { - "value": true - }, "allowRemoteVnetTraffic": { "value": true }, @@ -752,7 +1417,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway:

-### Example 7: _WAF-aligned_ +### Example 12: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -766,12 +1431,23 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: name: 'virtualNetworkGatewayDeployment' params: { // Required parameters + clusterSettings: { + asn: 65515 + clusterMode: 'activeActiveBgp' + customBgpIpAddresses: [ + '169.254.21.4' + '169.254.21.5' + ] + secondCustomBgpIpAddresses: [ + '169.254.22.4' + '169.254.22.5' + ] + } gatewayType: 'Vpn' name: 'nvgmwaf001' skuName: 'VpnGw2AZ' vNetResourceId: '' // Non-required parameters - activeActive: true allowRemoteVnetTraffic: true diagnosticSettings: [ { @@ -862,6 +1538,20 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "contentVersion": "1.0.0.0", "parameters": { // Required parameters + "clusterSettings": { + "value": { + "asn": 65515, + "clusterMode": "activeActiveBgp", + "customBgpIpAddresses": [ + "169.254.21.4", + "169.254.21.5" + ], + "secondCustomBgpIpAddresses": [ + "169.254.22.4", + "169.254.22.5" + ] + } + }, "gatewayType": { "value": "Vpn" }, @@ -875,9 +1565,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "value": "" }, // Non-required parameters - "activeActive": { - "value": true - }, "allowRemoteVnetTraffic": { "value": true }, @@ -992,6 +1679,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: | Parameter | Type | Description | | :-- | :-- | :-- | +| [`clusterSettings`](#parameter-clustersettings) | object | Specifies one of the following four configurations: Active-Active with (clusterMode = activeActiveBgp) or without (clusterMode = activeActiveNoBgp) BGP, Active-Passive with (clusterMode = activePassiveBgp) or without (clusterMode = activePassiveNoBgp) BGP. | | [`gatewayType`](#parameter-gatewaytype) | string | Specifies the gateway type. E.g. VPN, ExpressRoute. | | [`name`](#parameter-name) | string | Specifies the Virtual Network Gateway name. | | [`skuName`](#parameter-skuname) | string | The SKU of the Gateway. | @@ -1001,17 +1689,13 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: | Parameter | Type | Description | | :-- | :-- | :-- | -| [`activeActive`](#parameter-activeactive) | bool | Value to specify if the Gateway should be deployed in active-active or active-passive configuration. | -| [`activeGatewayPipName`](#parameter-activegatewaypipname) | string | 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`](#parameter-allowremotevnettraffic) | bool | Configure this gateway to accept traffic from other Azure Virtual Networks. This configuration does not support connectivity to Azure Virtual WAN. | | [`allowVirtualWanTraffic`](#parameter-allowvirtualwantraffic) | bool | Configures this gateway to accept traffic from remote Virtual WAN networks. | -| [`asn`](#parameter-asn) | int | ASN value. | | [`clientRevokedCertThumbprint`](#parameter-clientrevokedcertthumbprint) | string | Thumbprint of the revoked certificate. This would revoke VPN client certificates matching this thumbprint from connecting to the VNet. | | [`clientRootCertData`](#parameter-clientrootcertdata) | string | Client root certificate data used to authenticate VPN clients. Cannot be configured if vpnClientAadConfiguration is provided. | | [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | | [`disableIPSecReplayProtection`](#parameter-disableipsecreplayprotection) | bool | disableIPSecReplayProtection flag. Used for VPN Gateways. | | [`domainNameLabel`](#parameter-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`](#parameter-enablebgp) | bool | Value to specify if BGP is enabled or not. | | [`enableBgpRouteTranslationForNat`](#parameter-enablebgproutetranslationfornat) | bool | EnableBgpRouteTranslationForNat flag. Can only be used when "natRules" are enabled on the Virtual Network Gateway. | | [`enableDnsForwarding`](#parameter-enablednsforwarding) | bool | 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`](#parameter-enableprivateipaddress) | bool | 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. | @@ -1031,6 +1715,13 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: | [`vpnGatewayGeneration`](#parameter-vpngatewaygeneration) | string | The generation for this VirtualNetworkGateway. Must be None if virtualNetworkGatewayType is not VPN. | | [`vpnType`](#parameter-vpntype) | string | Specifies the VPN type. | +### Parameter: `clusterSettings` + +Specifies one of the following four configurations: Active-Active with (clusterMode = activeActiveBgp) or without (clusterMode = activeActiveNoBgp) BGP, Active-Passive with (clusterMode = activePassiveBgp) or without (clusterMode = activePassiveNoBgp) BGP. + +- Required: Yes +- Type: object + ### Parameter: `gatewayType` Specifies the gateway type. E.g. VPN, ExpressRoute. @@ -1088,22 +1779,6 @@ Virtual Network resource ID. - Required: Yes - Type: string -### Parameter: `activeActive` - -Value to specify if the Gateway should be deployed in active-active or active-passive configuration. - -- Required: No -- Type: bool -- Default: `True` - -### Parameter: `activeGatewayPipName` - -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. - -- Required: No -- Type: string -- Default: `[format('{0}-pip2', parameters('name'))]` - ### Parameter: `allowRemoteVnetTraffic` Configure this gateway to accept traffic from other Azure Virtual Networks. This configuration does not support connectivity to Azure Virtual WAN. @@ -1120,14 +1795,6 @@ Configures this gateway to accept traffic from remote Virtual WAN networks. - Type: bool - Default: `False` -### Parameter: `asn` - -ASN value. - -- Required: No -- Type: int -- Default: `65815` - ### Parameter: `clientRevokedCertThumbprint` Thumbprint of the revoked certificate. This would revoke VPN client certificates matching this thumbprint from connecting to the VNet. @@ -1306,14 +1973,6 @@ DNS name(s) of the Public IP resource(s). If you enabled active-active configura - Type: array - Default: `[]` -### Parameter: `enableBgp` - -Value to specify if BGP is enabled or not. - -- Required: No -- Type: bool -- Default: `True` - ### Parameter: `enableBgpRouteTranslationForNat` EnableBgpRouteTranslationForNat flag. Can only be used when "natRules" are enabled on the Virtual Network Gateway. diff --git a/avm/res/network/virtual-network-gateway/main.bicep b/avm/res/network/virtual-network-gateway/main.bicep index 6b73abca5d..15b0247f69 100644 --- a/avm/res/network/virtual-network-gateway/main.bicep +++ b/avm/res/network/virtual-network-gateway/main.bicep @@ -11,9 +11,6 @@ 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 = '' @@ -70,14 +67,8 @@ 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('Required. Specifies one of the following four configurations: Active-Active with (clusterMode = activeActiveBgp) or without (clusterMode = activeActiveNoBgp) BGP, Active-Passive with (clusterMode = activePassiveBgp) or without (clusterMode = activePassiveNoBgp) BGP.') +param clusterSettings clusterSettingType @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 = '' @@ -140,25 +131,53 @@ param vpnClientAadConfiguration object = {} // Other Variables var gatewayPipAllocationMethod = skuName == 'Basic' ? 'Dynamic' : 'Static' -var isActiveActiveValid = gatewayType != 'ExpressRoute' ? activeActive : false -var virtualGatewayPipNameVar = isActiveActiveValid +var isExpressRoute = gatewayType == 'ExpressRoute' + +var vpnTypeVar = !isExpressRoute ? vpnType : 'PolicyBased' + +var isBgp = (clusterSettings.clusterMode == 'activeActiveBgp' || clusterSettings.clusterMode == 'activePassiveBgp') && !isExpressRoute + +var isActiveActive = (clusterSettings.clusterMode == 'activeActiveNoBgp' || clusterSettings.clusterMode == 'activeActiveBgp') && !isExpressRoute + +var activeGatewayPipNameVar = isActiveActive ? (clusterSettings.?activeGatewayPipName ?? '${name}-pip2') : null + +var virtualGatewayPipNameVar = isActiveActive ? [ gatewayPipName - activeGatewayPipName + activeGatewayPipNameVar ] : [ gatewayPipName ] -var vpnTypeVar = gatewayType != 'ExpressRoute' ? vpnType : 'PolicyBased' +// Potential BGP configurations (active-active vs active-passive) +var bgpSettingsVar = isActiveActive + ? { + asn: clusterSettings.?asn ?? 65515 + bgpPeeringAddresses: [ + { + customBgpIpAddresses: clusterSettings.?customBgpIpAddresses + ipconfigurationId: '${az.resourceId('Microsoft.Network/virtualNetworkGateways', name)}/ipConfigurations/vNetGatewayConfig1' + } + { + customBgpIpAddresses: clusterSettings.?secondCustomBgpIpAddresses + ipconfigurationId: '${az.resourceId('Microsoft.Network/virtualNetworkGateways', name)}/ipConfigurations/vNetGatewayConfig2' + } + ] + } + : { + asn: clusterSettings.?asn ?? 65515 + bgpPeeringAddresses: [ + { + customBgpIpAddresses: clusterSettings.?customBgpIpAddresses + ipconfigurationId: '${az.resourceId('Microsoft.Network/virtualNetworkGateways', name)}/ipConfigurations/vNetGatewayConfig1' + } + ] + } -var isBgpValid = gatewayType != 'ExpressRoute' ? enableBgp : false -var bgpSettings = { - asn: asn -} -// Potential configurations (active-active vs active-passive) -var ipConfiguration = isActiveActiveValid +// Potential IP configurations (active-active vs active-passive) +var ipConfiguration = isActiveActive ? [ { properties: { @@ -179,8 +198,8 @@ var ipConfiguration = isActiveActiveValid id: '${vNetResourceId}/subnets/GatewaySubnet' } publicIPAddress: { - id: isActiveActiveValid - ? az.resourceId('Microsoft.Network/publicIPAddresses', activeGatewayPipName) + id: isActiveActive + ? az.resourceId('Microsoft.Network/publicIPAddresses', activeGatewayPipNameVar) : az.resourceId('Microsoft.Network/publicIPAddresses', gatewayPipName) } } @@ -331,13 +350,13 @@ resource virtualNetworkGateway 'Microsoft.Network/virtualNetworkGateways@2023-04 tags: tags properties: { ipConfigurations: ipConfiguration - activeActive: isActiveActiveValid + activeActive: isActiveActive allowRemoteVnetTraffic: allowRemoteVnetTraffic allowVirtualWanTraffic: allowVirtualWanTraffic - enableBgp: isBgpValid - bgpSettings: isBgpValid ? bgpSettings : null + enableBgp: isBgp + bgpSettings: isBgp ? bgpSettingsVar : null disableIPSecReplayProtection: disableIPSecReplayProtection - enableDnsForwarding: gatewayType == 'ExpressRoute' ? enableDnsForwarding : null + enableDnsForwarding: !isExpressRoute ? enableDnsForwarding : null enablePrivateIpAddress: enablePrivateIpAddress enableBgpRouteTranslationForNat: enableBgpRouteTranslationForNat gatewayType: gatewayType @@ -365,11 +384,11 @@ module virtualNetworkGateway_natRules 'nat-rule/main.bicep' = [ 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 : '' + externalMappings: natRule.?externalMappings ?? [] + internalMappings: natRule.?internalMappings ?? [] + ipConfigurationId: natRule.?ipConfigurationId ?? '' + mode: natRule.?mode ?? '' + type: natRule.?type ?? '' } } ] @@ -533,3 +552,55 @@ type diagnosticSettingType = { @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type activePassiveNoBgpType = { + + clusterMode: 'activePassiveNoBgp' + +} + +type activeActiveNoBgpType = { + + clusterMode: 'activeActiveNoBgp' + + @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 \'-pip2\' suffix will be appended to the gateway\'s name.') + activeGatewayPipName: string? + +} + +type activePassiveBgpType = { + + clusterMode: 'activePassiveBgp' + + @description('Optional. The Autonomous System Number value. If it\'s not provided, a default \'65515\' value will be assigned to the ASN.') + @minValue(0) + @maxValue(4294967295) + asn: int? + + @description('Optional. The list of custom BGP IP Address (APIPA) peering addresses which belong to IP configuration.') + customBgpIpAddresses: string[]? +} + +type activeActiveBgpType = { + + clusterMode: 'activeActiveBgp' + + @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 \'-pip2\' suffix will be appended to the gateway\'s name.') + activeGatewayPipName: string? + + @description('Optional. The Autonomous System Number value. If it\'s not provided, a default \'65515\' value will be assigned to the ASN.') + @minValue(0) + @maxValue(4294967295) + asn: int? + + @description('Optional. The list of custom BGP IP Address (APIPA) peering addresses which belong to IP configuration.') + customBgpIpAddresses: string[]? + + @description('Optional. The list of the second custom BGP IP Address (APIPA) peering addresses which belong to IP configuration.') + secondCustomBgpIpAddresses: string[]? +} + +@discriminator('clusterMode') +type clusterSettingType = activeActiveNoBgpType | activeActiveBgpType | activePassiveBgpType | activePassiveNoBgpType + + diff --git a/avm/res/network/virtual-network-gateway/main.json b/avm/res/network/virtual-network-gateway/main.json index 19876a6210..5e1b4bdf4c 100644 --- a/avm/res/network/virtual-network-gateway/main.json +++ b/avm/res/network/virtual-network-gateway/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "15596993518588024968" + "templateHash": "973776533492793692" }, "name": "Virtual Network Gateways", "description": "This module deploys a Virtual Network Gateway.", @@ -230,6 +230,132 @@ } }, "nullable": true + }, + "activePassiveNoBgpType": { + "type": "object", + "properties": { + "clusterMode": { + "type": "string", + "allowedValues": [ + "activePassiveNoBgp" + ] + } + } + }, + "activeActiveNoBgpType": { + "type": "object", + "properties": { + "clusterMode": { + "type": "string", + "allowedValues": [ + "activeActiveNoBgp" + ] + }, + "activeGatewayPipName": { + "type": "string", + "nullable": true, + "metadata": { + "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 '-pip2' suffix will be appended to the gateway's name." + } + } + } + }, + "activePassiveBgpType": { + "type": "object", + "properties": { + "clusterMode": { + "type": "string", + "allowedValues": [ + "activePassiveBgp" + ] + }, + "asn": { + "type": "int", + "nullable": true, + "minValue": 0, + "maxValue": 4294967295, + "metadata": { + "description": "Optional. The Autonomous System Number value. If it's not provided, a default '65515' value will be assigned to the ASN." + } + }, + "customBgpIpAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of custom BGP IP Address (APIPA) peering addresses which belong to IP configuration." + } + } + } + }, + "activeActiveBgpType": { + "type": "object", + "properties": { + "clusterMode": { + "type": "string", + "allowedValues": [ + "activeActiveBgp" + ] + }, + "activeGatewayPipName": { + "type": "string", + "nullable": true, + "metadata": { + "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 '-pip2' suffix will be appended to the gateway's name." + } + }, + "asn": { + "type": "int", + "nullable": true, + "minValue": 0, + "maxValue": 4294967295, + "metadata": { + "description": "Optional. The Autonomous System Number value. If it's not provided, a default '65515' value will be assigned to the ASN." + } + }, + "customBgpIpAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of custom BGP IP Address (APIPA) peering addresses which belong to IP configuration." + } + }, + "secondCustomBgpIpAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The list of the second custom BGP IP Address (APIPA) peering addresses which belong to IP configuration." + } + } + } + }, + "clusterSettingType": { + "type": "object", + "discriminator": { + "propertyName": "clusterMode", + "mapping": { + "activeActiveNoBgp": { + "$ref": "#/definitions/activeActiveNoBgpType" + }, + "activeActiveBgp": { + "$ref": "#/definitions/activeActiveBgpType" + }, + "activePassiveBgp": { + "$ref": "#/definitions/activePassiveBgpType" + }, + "activePassiveNoBgp": { + "$ref": "#/definitions/activePassiveNoBgpType" + } + } + } } }, "parameters": { @@ -253,13 +379,6 @@ "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." } }, - "activeGatewayPipName": { - "type": "string", - "defaultValue": "[format('{0}-pip2', parameters('name'))]", - "metadata": { - "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." - } - }, "publicIPPrefixResourceId": { "type": "string", "defaultValue": "", @@ -345,25 +464,10 @@ "description": "Required. Virtual Network resource ID." } }, - "activeActive": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Value to specify if the Gateway should be deployed in active-active or active-passive configuration." - } - }, - "enableBgp": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Optional. Value to specify if BGP is enabled or not." - } - }, - "asn": { - "type": "int", - "defaultValue": 65815, + "clusterSettings": { + "$ref": "#/definitions/clusterSettingType", "metadata": { - "description": "Optional. ASN value." + "description": "Required. Specifies one of the following four configurations: Active-Active with (clusterMode = activeActiveBgp) or without (clusterMode = activeActiveNoBgp) BGP, Active-Passive with (clusterMode = activePassiveBgp) or without (clusterMode = activePassiveNoBgp) BGP." } }, "vpnClientAddressPoolPrefix": { @@ -498,14 +602,14 @@ } ], "gatewayPipAllocationMethod": "[if(equals(parameters('skuName'), 'Basic'), 'Dynamic', 'Static')]", - "isActiveActiveValid": "[if(not(equals(parameters('gatewayType'), 'ExpressRoute')), parameters('activeActive'), false())]", - "virtualGatewayPipNameVar": "[if(variables('isActiveActiveValid'), createArray(parameters('gatewayPipName'), parameters('activeGatewayPipName')), createArray(parameters('gatewayPipName')))]", - "vpnTypeVar": "[if(not(equals(parameters('gatewayType'), 'ExpressRoute')), parameters('vpnType'), 'PolicyBased')]", - "isBgpValid": "[if(not(equals(parameters('gatewayType'), 'ExpressRoute')), parameters('enableBgp'), false())]", - "bgpSettings": { - "asn": "[parameters('asn')]" - }, - "ipConfiguration": "[if(variables('isActiveActiveValid'), createArray(createObject('properties', createObject('privateIPAllocationMethod', 'Dynamic', 'subnet', createObject('id', format('{0}/subnets/GatewaySubnet', parameters('vNetResourceId'))), 'publicIPAddress', createObject('id', resourceId('Microsoft.Network/publicIPAddresses', parameters('gatewayPipName')))), 'name', 'vNetGatewayConfig1'), createObject('properties', createObject('privateIPAllocationMethod', 'Dynamic', 'subnet', createObject('id', format('{0}/subnets/GatewaySubnet', parameters('vNetResourceId'))), 'publicIPAddress', createObject('id', if(variables('isActiveActiveValid'), resourceId('Microsoft.Network/publicIPAddresses', parameters('activeGatewayPipName')), resourceId('Microsoft.Network/publicIPAddresses', parameters('gatewayPipName'))))), 'name', 'vNetGatewayConfig2')), createArray(createObject('properties', createObject('privateIPAllocationMethod', 'Dynamic', 'subnet', createObject('id', format('{0}/subnets/GatewaySubnet', parameters('vNetResourceId'))), 'publicIPAddress', createObject('id', resourceId('Microsoft.Network/publicIPAddresses', parameters('gatewayPipName')))), 'name', 'vNetGatewayConfig1')))]", + "isExpressRoute": "[equals(parameters('gatewayType'), 'ExpressRoute')]", + "vpnTypeVar": "[if(not(variables('isExpressRoute')), parameters('vpnType'), 'PolicyBased')]", + "isBgp": "[and(or(equals(parameters('clusterSettings').clusterMode, 'activeActiveBgp'), equals(parameters('clusterSettings').clusterMode, 'activePassiveBgp')), not(variables('isExpressRoute')))]", + "isActiveActive": "[and(or(equals(parameters('clusterSettings').clusterMode, 'activeActiveNoBgp'), equals(parameters('clusterSettings').clusterMode, 'activeActiveBgp')), not(variables('isExpressRoute')))]", + "activeGatewayPipNameVar": "[if(variables('isActiveActive'), coalesce(tryGet(parameters('clusterSettings'), 'activeGatewayPipName'), format('{0}-pip2', parameters('name'))), null())]", + "virtualGatewayPipNameVar": "[if(variables('isActiveActive'), createArray(parameters('gatewayPipName'), variables('activeGatewayPipNameVar')), createArray(parameters('gatewayPipName')))]", + "bgpSettingsVar": "[if(variables('isActiveActive'), createObject('asn', coalesce(tryGet(parameters('clusterSettings'), 'asn'), 65515), 'bgpPeeringAddresses', createArray(createObject('customBgpIpAddresses', tryGet(parameters('clusterSettings'), 'customBgpIpAddresses'), 'ipconfigurationId', format('{0}/ipConfigurations/vNetGatewayConfig1', resourceId('Microsoft.Network/virtualNetworkGateways', parameters('name')))), createObject('customBgpIpAddresses', tryGet(parameters('clusterSettings'), 'secondCustomBgpIpAddresses'), 'ipconfigurationId', format('{0}/ipConfigurations/vNetGatewayConfig2', resourceId('Microsoft.Network/virtualNetworkGateways', parameters('name')))))), createObject('asn', coalesce(tryGet(parameters('clusterSettings'), 'asn'), 65515), 'bgpPeeringAddresses', createArray(createObject('customBgpIpAddresses', tryGet(parameters('clusterSettings'), 'customBgpIpAddresses'), 'ipconfigurationId', format('{0}/ipConfigurations/vNetGatewayConfig1', resourceId('Microsoft.Network/virtualNetworkGateways', parameters('name')))))))]", + "ipConfiguration": "[if(variables('isActiveActive'), createArray(createObject('properties', createObject('privateIPAllocationMethod', 'Dynamic', 'subnet', createObject('id', format('{0}/subnets/GatewaySubnet', parameters('vNetResourceId'))), 'publicIPAddress', createObject('id', resourceId('Microsoft.Network/publicIPAddresses', parameters('gatewayPipName')))), 'name', 'vNetGatewayConfig1'), createObject('properties', createObject('privateIPAllocationMethod', 'Dynamic', 'subnet', createObject('id', format('{0}/subnets/GatewaySubnet', parameters('vNetResourceId'))), 'publicIPAddress', createObject('id', if(variables('isActiveActive'), resourceId('Microsoft.Network/publicIPAddresses', variables('activeGatewayPipNameVar')), resourceId('Microsoft.Network/publicIPAddresses', parameters('gatewayPipName'))))), 'name', 'vNetGatewayConfig2')), createArray(createObject('properties', createObject('privateIPAllocationMethod', 'Dynamic', 'subnet', createObject('id', format('{0}/subnets/GatewaySubnet', parameters('vNetResourceId'))), 'publicIPAddress', createObject('id', resourceId('Microsoft.Network/publicIPAddresses', parameters('gatewayPipName')))), 'name', 'vNetGatewayConfig1')))]", "vpnClientConfiguration": "[if(not(empty(parameters('clientRootCertData'))), createObject('vpnClientAddressPool', createObject('addressPrefixes', createArray(parameters('vpnClientAddressPoolPrefix'))), 'vpnClientRootCertificates', createArray(createObject('name', 'RootCert1', 'properties', createObject('publicCertData', parameters('clientRootCertData')))), 'vpnClientRevokedCertificates', if(not(empty(parameters('clientRevokedCertThumbprint'))), createArray(createObject('name', 'RevokedCert1', 'properties', createObject('thumbprint', parameters('clientRevokedCertThumbprint')))), null())), if(not(empty(parameters('vpnClientAadConfiguration'))), createObject('vpnClientAddressPool', createObject('addressPrefixes', createArray(parameters('vpnClientAddressPoolPrefix'))), 'aadTenant', parameters('vpnClientAadConfiguration').aadTenant, 'aadAudience', parameters('vpnClientAadConfiguration').aadAudience, 'aadIssuer', parameters('vpnClientAadConfiguration').aadIssuer, 'vpnAuthenticationTypes', parameters('vpnClientAadConfiguration').vpnAuthenticationTypes, 'vpnClientProtocols', parameters('vpnClientAadConfiguration').vpnClientProtocols), null()))]", "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", @@ -545,13 +649,13 @@ "tags": "[parameters('tags')]", "properties": { "ipConfigurations": "[variables('ipConfiguration')]", - "activeActive": "[variables('isActiveActiveValid')]", + "activeActive": "[variables('isActiveActive')]", "allowRemoteVnetTraffic": "[parameters('allowRemoteVnetTraffic')]", "allowVirtualWanTraffic": "[parameters('allowVirtualWanTraffic')]", - "enableBgp": "[variables('isBgpValid')]", - "bgpSettings": "[if(variables('isBgpValid'), variables('bgpSettings'), null())]", + "enableBgp": "[variables('isBgp')]", + "bgpSettings": "[if(variables('isBgp'), variables('bgpSettingsVar'), null())]", "disableIPSecReplayProtection": "[parameters('disableIPSecReplayProtection')]", - "enableDnsForwarding": "[if(equals(parameters('gatewayType'), 'ExpressRoute'), parameters('enableDnsForwarding'), null())]", + "enableDnsForwarding": "[if(not(variables('isExpressRoute')), parameters('enableDnsForwarding'), null())]", "enablePrivateIpAddress": "[parameters('enablePrivateIpAddress')]", "enableBgpRouteTranslationForNat": "[parameters('enableBgpRouteTranslationForNat')]", "gatewayType": "[parameters('gatewayType')]", @@ -1329,11 +1433,21 @@ "virtualNetworkGatewayName": { "value": "[parameters('name')]" }, - "externalMappings": "[if(contains(parameters('natRules')[copyIndex()], 'externalMappings'), createObject('value', parameters('natRules')[copyIndex()].externalMappings), createObject('value', createArray()))]", - "internalMappings": "[if(contains(parameters('natRules')[copyIndex()], 'internalMappings'), createObject('value', parameters('natRules')[copyIndex()].internalMappings), createObject('value', createArray()))]", - "ipConfigurationId": "[if(contains(parameters('natRules')[copyIndex()], 'ipConfigurationId'), createObject('value', parameters('natRules')[copyIndex()].ipConfigurationId), createObject('value', ''))]", - "mode": "[if(contains(parameters('natRules')[copyIndex()], 'mode'), createObject('value', parameters('natRules')[copyIndex()].mode), createObject('value', ''))]", - "type": "[if(contains(parameters('natRules')[copyIndex()], 'type'), createObject('value', parameters('natRules')[copyIndex()].type), createObject('value', ''))]" + "externalMappings": { + "value": "[coalesce(tryGet(parameters('natRules')[copyIndex()], 'externalMappings'), createArray())]" + }, + "internalMappings": { + "value": "[coalesce(tryGet(parameters('natRules')[copyIndex()], 'internalMappings'), createArray())]" + }, + "ipConfigurationId": { + "value": "[coalesce(tryGet(parameters('natRules')[copyIndex()], 'ipConfigurationId'), '')]" + }, + "mode": { + "value": "[coalesce(tryGet(parameters('natRules')[copyIndex()], 'mode'), '')]" + }, + "type": { + "value": "[coalesce(tryGet(parameters('natRules')[copyIndex()], 'type'), '')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/aadvpn/main.test.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/aadvpn/main.test.bicep index 0b4425a9a0..09457de2bd 100644 --- a/avm/res/network/virtual-network-gateway/tests/e2e/aadvpn/main.test.bicep +++ b/avm/res/network/virtual-network-gateway/tests/e2e/aadvpn/main.test.bicep @@ -15,7 +15,7 @@ param resourceGroupName string = 'dep-${namePrefix}-network.virtualnetworkgatewa param resourceLocation string = deployment().location @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') -param serviceShort string = 'nvngavpn' +param serviceShort string = 'nvgavpn' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' @@ -55,7 +55,9 @@ module testDeployment '../../../main.bicep' = [ skuName: 'VpnGw2AZ' gatewayType: 'Vpn' vNetResourceId: nestedDependencies.outputs.vnetResourceId - activeActive: false + clusterSettings:{ + clusterMode: 'activePassiveNoBgp' + } domainNameLabel: [ '${namePrefix}-dm-${serviceShort}' ] diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveBGP/dependencies.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveBGP/dependencies.bicep new file mode 100644 index 0000000000..c3aebf111c --- /dev/null +++ b/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveBGP/dependencies.bicep @@ -0,0 +1,49 @@ +@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 Local Network Gateway to create.') +param localNetworkGatewayName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'GatewaySubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource localNetworkGateway 'Microsoft.Network/localNetworkGateways@2023-04-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 resource ID of the created Local Network Gateway.') +output localNetworkGatewayResourceId string = localNetworkGateway.id diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveBGP/main.test.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveBGP/main.test.bicep new file mode 100644 index 0000000000..6270baf598 --- /dev/null +++ b/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveBGP/main.test.bicep @@ -0,0 +1,83 @@ +targetScope = 'subscription' + +metadata name = 'VPN Active Active with BGP settings' +metadata description = 'This instance deploys the module with the VPN Active Active with BGP settings.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.virtualnetworkgateways-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nvgaab' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + localNetworkGatewayName: 'dep-${namePrefix}-lng-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}${serviceShort}001' + vpnGatewayGeneration: 'Generation2' + skuName: 'VpnGw2AZ' + gatewayType: 'Vpn' + vNetResourceId: nestedDependencies.outputs.vnetResourceId + clusterSettings: { + clusterMode: 'activeActiveBgp' + } + + domainNameLabel: [ + '${namePrefix}-dm-${serviceShort}' + ] + publicIpZones: [ + 1 + 2 + 3 + ] + vpnType: 'RouteBased' + enablePrivateIpAddress: true + gatewayDefaultSiteLocalNetworkGatewayId: nestedDependencies.outputs.localNetworkGatewayResourceId + disableIPSecReplayProtection: true + allowRemoteVnetTraffic: true + enableBgpRouteTranslationForNat: true + } + dependsOn: [ + nestedDependencies + ] + } +] diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveBgpAPIPA/dependencies.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveBgpAPIPA/dependencies.bicep new file mode 100644 index 0000000000..c3aebf111c --- /dev/null +++ b/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveBgpAPIPA/dependencies.bicep @@ -0,0 +1,49 @@ +@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 Local Network Gateway to create.') +param localNetworkGatewayName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'GatewaySubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource localNetworkGateway 'Microsoft.Network/localNetworkGateways@2023-04-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 resource ID of the created Local Network Gateway.') +output localNetworkGatewayResourceId string = localNetworkGateway.id diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveBgpAPIPA/main.test.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveBgpAPIPA/main.test.bicep new file mode 100644 index 0000000000..16fe844184 --- /dev/null +++ b/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveBgpAPIPA/main.test.bicep @@ -0,0 +1,85 @@ +targetScope = 'subscription' + +metadata name = 'VPN Active Active with BGP settings' +metadata description = 'This instance deploys the module with the VPN Active Active with APIPA BGP settings.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.virtualnetworkgateways-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nvgaaa' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + localNetworkGatewayName: 'dep-${namePrefix}-lng-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}${serviceShort}001' + vpnGatewayGeneration: 'Generation2' + skuName: 'VpnGw2AZ' + gatewayType: 'Vpn' + vNetResourceId: nestedDependencies.outputs.vnetResourceId + clusterSettings: { + clusterMode: 'activeActiveBgp' + customBgpIpAddresses: ['169.254.21.4','169.254.21.5'] + secondCustomBgpIpAddresses: ['169.254.22.4','169.254.22.5'] + } + + domainNameLabel: [ + '${namePrefix}-dm-${serviceShort}' + ] + publicIpZones: [ + 1 + 2 + 3 + ] + vpnType: 'RouteBased' + enablePrivateIpAddress: true + gatewayDefaultSiteLocalNetworkGatewayId: nestedDependencies.outputs.localNetworkGatewayResourceId + disableIPSecReplayProtection: true + allowRemoteVnetTraffic: true + enableBgpRouteTranslationForNat: true + } + dependsOn: [ + nestedDependencies + ] + } +] diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveNoBGP/dependencies.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveNoBGP/dependencies.bicep new file mode 100644 index 0000000000..c3aebf111c --- /dev/null +++ b/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveNoBGP/dependencies.bicep @@ -0,0 +1,49 @@ +@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 Local Network Gateway to create.') +param localNetworkGatewayName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'GatewaySubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource localNetworkGateway 'Microsoft.Network/localNetworkGateways@2023-04-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 resource ID of the created Local Network Gateway.') +output localNetworkGatewayResourceId string = localNetworkGateway.id diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveNoBGP/main.test.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveNoBGP/main.test.bicep new file mode 100644 index 0000000000..7d799c5c2c --- /dev/null +++ b/avm/res/network/virtual-network-gateway/tests/e2e/activeActiveNoBGP/main.test.bicep @@ -0,0 +1,83 @@ +targetScope = 'subscription' + +metadata name = 'VPN Active Active without BGP settings' +metadata description = 'This instance deploys the module with the VPN Active Active without BGP settings.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.virtualnetworkgateways-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nvgaa' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + localNetworkGatewayName: 'dep-${namePrefix}-lng-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}${serviceShort}001' + vpnGatewayGeneration: 'Generation2' + skuName: 'VpnGw2AZ' + gatewayType: 'Vpn' + vNetResourceId: nestedDependencies.outputs.vnetResourceId + clusterSettings: { + clusterMode: 'activeActiveNoBgp' + } + + domainNameLabel: [ + '${namePrefix}-dm-${serviceShort}' + ] + publicIpZones: [ + 1 + 2 + 3 + ] + vpnType: 'RouteBased' + enablePrivateIpAddress: true + gatewayDefaultSiteLocalNetworkGatewayId: nestedDependencies.outputs.localNetworkGatewayResourceId + disableIPSecReplayProtection: true + allowRemoteVnetTraffic: true + enableBgpRouteTranslationForNat: true + } + dependsOn: [ + nestedDependencies + ] + } +] diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/activePassiveBGP/dependencies.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/activePassiveBGP/dependencies.bicep new file mode 100644 index 0000000000..c3aebf111c --- /dev/null +++ b/avm/res/network/virtual-network-gateway/tests/e2e/activePassiveBGP/dependencies.bicep @@ -0,0 +1,49 @@ +@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 Local Network Gateway to create.') +param localNetworkGatewayName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'GatewaySubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource localNetworkGateway 'Microsoft.Network/localNetworkGateways@2023-04-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 resource ID of the created Local Network Gateway.') +output localNetworkGatewayResourceId string = localNetworkGateway.id diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/activePassiveBGP/main.test.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/activePassiveBGP/main.test.bicep new file mode 100644 index 0000000000..1aa9da7c34 --- /dev/null +++ b/avm/res/network/virtual-network-gateway/tests/e2e/activePassiveBGP/main.test.bicep @@ -0,0 +1,85 @@ +targetScope = 'subscription' + +metadata name = 'VPN Active Passive with BGP settings' +metadata description = 'This instance deploys the module with the VPN Active Passive with APIPA BGP settings.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.virtualnetworkgateways-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nvgapb' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + localNetworkGatewayName: 'dep-${namePrefix}-lng-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}${serviceShort}001' + vpnGatewayGeneration: 'Generation2' + skuName: 'VpnGw2AZ' + gatewayType: 'Vpn' + vNetResourceId: nestedDependencies.outputs.vnetResourceId + clusterSettings: { + clusterMode:'activePassiveBgp' + customBgpIpAddresses: ['169.254.21.4','169.254.21.5'] + asn: 65815 + } + + domainNameLabel: [ + '${namePrefix}-dm-${serviceShort}' + ] + publicIpZones: [ + 1 + 2 + 3 + ] + vpnType: 'RouteBased' + enablePrivateIpAddress: true + gatewayDefaultSiteLocalNetworkGatewayId: nestedDependencies.outputs.localNetworkGatewayResourceId + disableIPSecReplayProtection: true + allowRemoteVnetTraffic: true + enableBgpRouteTranslationForNat: true + } + dependsOn: [ + nestedDependencies + ] + } +] diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/activePassiveNoBGP/dependencies.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/activePassiveNoBGP/dependencies.bicep new file mode 100644 index 0000000000..c3aebf111c --- /dev/null +++ b/avm/res/network/virtual-network-gateway/tests/e2e/activePassiveNoBGP/dependencies.bicep @@ -0,0 +1,49 @@ +@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 Local Network Gateway to create.') +param localNetworkGatewayName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'GatewaySubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource localNetworkGateway 'Microsoft.Network/localNetworkGateways@2023-04-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 resource ID of the created Local Network Gateway.') +output localNetworkGatewayResourceId string = localNetworkGateway.id diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/activePassiveNoBGP/main.test.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/activePassiveNoBGP/main.test.bicep new file mode 100644 index 0000000000..ee3904f4b8 --- /dev/null +++ b/avm/res/network/virtual-network-gateway/tests/e2e/activePassiveNoBGP/main.test.bicep @@ -0,0 +1,83 @@ +targetScope = 'subscription' + +metadata name = 'VPN Active Passive without BGP settings' +metadata description = 'This instance deploys the module with the VPN Active Passive without BGP settings.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.virtualnetworkgateways-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nvgap' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + localNetworkGatewayName: 'dep-${namePrefix}-lng-${serviceShort}' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}${serviceShort}001' + vpnGatewayGeneration: 'Generation2' + skuName: 'VpnGw2AZ' + gatewayType: 'Vpn' + vNetResourceId: nestedDependencies.outputs.vnetResourceId + clusterSettings: { + clusterMode:'activePassiveNoBgp' + } + + domainNameLabel: [ + '${namePrefix}-dm-${serviceShort}' + ] + publicIpZones: [ + 1 + 2 + 3 + ] + vpnType: 'RouteBased' + enablePrivateIpAddress: true + gatewayDefaultSiteLocalNetworkGatewayId: nestedDependencies.outputs.localNetworkGatewayResourceId + disableIPSecReplayProtection: true + allowRemoteVnetTraffic: true + enableBgpRouteTranslationForNat: true + } + dependsOn: [ + nestedDependencies + ] + } +] diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/defaults/main.test.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/defaults/main.test.bicep index 9e5f80b30c..06f382ecb5 100644 --- a/avm/res/network/virtual-network-gateway/tests/e2e/defaults/main.test.bicep +++ b/avm/res/network/virtual-network-gateway/tests/e2e/defaults/main.test.bicep @@ -62,6 +62,9 @@ module testDeployment '../../../main.bicep' = [ 2 3 ] + clusterSettings: { + clusterMode:'activeActiveNoBgp' + } } dependsOn: [ nestedDependencies diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/expressRoute/main.test.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/expressRoute/main.test.bicep index 22b116891b..600e374025 100644 --- a/avm/res/network/virtual-network-gateway/tests/e2e/expressRoute/main.test.bicep +++ b/avm/res/network/virtual-network-gateway/tests/e2e/expressRoute/main.test.bicep @@ -55,6 +55,9 @@ module testDeployment '../../../main.bicep' = [ skuName: 'ErGw1AZ' gatewayType: 'ExpressRoute' vNetResourceId: nestedDependencies.outputs.vnetResourceId + clusterSettings:{ + clusterMode: 'activePassiveBgp' + } domainNameLabel: [ '${namePrefix}-dm-${serviceShort}' ] diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/max/main.test.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/max/main.test.bicep index 346b380610..b0314fa4dc 100644 --- a/avm/res/network/virtual-network-gateway/tests/e2e/max/main.test.bicep +++ b/avm/res/network/virtual-network-gateway/tests/e2e/max/main.test.bicep @@ -72,7 +72,12 @@ module testDeployment '../../../main.bicep' = [ skuName: 'VpnGw2AZ' gatewayType: 'Vpn' vNetResourceId: nestedDependencies.outputs.vnetResourceId - activeActive: true + clusterSettings:{ + clusterMode: 'activeActiveBgp' + activeGatewayPipName: '${namePrefix}${serviceShort}001-pip2' + customBgpIpAddresses: ['169.254.21.4','169.254.21.5'] + secondCustomBgpIpAddresses: ['169.254.22.4','169.254.22.5'] + } diagnosticSettings: [ { name: 'customSetting' diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/vpn-no-az/main.test.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/vpn-no-az/main.test.bicep index 7966ff4a3d..514b8429ab 100644 --- a/avm/res/network/virtual-network-gateway/tests/e2e/vpn-no-az/main.test.bicep +++ b/avm/res/network/virtual-network-gateway/tests/e2e/vpn-no-az/main.test.bicep @@ -57,6 +57,9 @@ module testDeployment '../../../main.bicep' = [ skuName: 'VpnGw1' gatewayType: 'Vpn' vNetResourceId: nestedDependencies.outputs.vnetResourceId + clusterSettings:{ + clusterMode: 'activePassiveNoBgp' + } } dependsOn: [ nestedDependencies diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/vpn/main.test.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/vpn/main.test.bicep index 6ac66acfd1..4258c36c8a 100644 --- a/avm/res/network/virtual-network-gateway/tests/e2e/vpn/main.test.bicep +++ b/avm/res/network/virtual-network-gateway/tests/e2e/vpn/main.test.bicep @@ -57,7 +57,9 @@ module testDeployment '../../../main.bicep' = [ skuName: 'VpnGw2AZ' gatewayType: 'Vpn' vNetResourceId: nestedDependencies.outputs.vnetResourceId - activeActive: true + clusterSettings:{ + clusterMode: 'activeActiveNoBgp' + } domainNameLabel: [ '${namePrefix}-dm-${serviceShort}' ] diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/waf-aligned/main.test.bicep index 8a31ac21fc..e0da5f11c5 100644 --- a/avm/res/network/virtual-network-gateway/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/network/virtual-network-gateway/tests/e2e/waf-aligned/main.test.bicep @@ -70,7 +70,12 @@ module testDeployment '../../../main.bicep' = [ skuName: 'VpnGw2AZ' gatewayType: 'Vpn' vNetResourceId: nestedDependencies.outputs.vnetResourceId - activeActive: true + clusterSettings: { + clusterMode:'activeActiveBgp' + customBgpIpAddresses: ['169.254.21.4','169.254.21.5'] + secondCustomBgpIpAddresses: ['169.254.22.4','169.254.22.5'] + asn: 65515 + } diagnosticSettings: [ { name: 'customSetting' diff --git a/avm/res/network/virtual-network-gateway/version.json b/avm/res/network/virtual-network-gateway/version.json index 76049e1c4a..13669e6601 100644 --- a/avm/res/network/virtual-network-gateway/version.json +++ b/avm/res/network/virtual-network-gateway/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.3", + "version": "0.4", "pathFilters": [ "./main.json" ] From f1d14ed950ed805b89fc2aeed75779c133a3ae31 Mon Sep 17 00:00:00 2001 From: John Date: Fri, 20 Sep 2024 01:26:11 +0200 Subject: [PATCH 28/57] fix: static validation error and BCP error in `avm.res.dev-ops-infrastructure.pool` (#3333) ## Description This pull request fixes the following: * The static validation is now passing, fixed by rerunning the Set-AVMModule command * BCP error regarding a property/parameter that cannot be null - fixed this by making it required Tagging @matebarabas @AlexanderSehr ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.dev-ops-infrastructure.pool](https://github.com/johnlokerse/bicep-registry-modules/actions/workflows/avm.res.dev-ops-infrastructure.pool.yml/badge.svg?branch=johnlokerse%2Ffix-staticvalidation-mdp)](https://github.com/johnlokerse/bicep-registry-modules/actions/workflows/avm.res.dev-ops-infrastructure.pool.yml) | ## Type of Change - [x] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- avm/res/dev-ops-infrastructure/pool/README.md | 41 +++++++++++-------- .../dev-ops-infrastructure/pool/main.bicep | 4 +- avm/res/dev-ops-infrastructure/pool/main.json | 7 ++-- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/avm/res/dev-ops-infrastructure/pool/README.md b/avm/res/dev-ops-infrastructure/pool/README.md index 4cd888f4b4..4ea945d1ab 100644 --- a/avm/res/dev-ops-infrastructure/pool/README.md +++ b/avm/res/dev-ops-infrastructure/pool/README.md @@ -8,7 +8,7 @@ This module deploys the Managed DevOps Pool resource. - [Usage examples](#Usage-examples) - [Parameters](#Parameters) - [Outputs](#Outputs) -- [Cross-referenced modules](#Cross-referenced-modules) +- [Notes](#Notes) - [Data Collection](#Data-Collection) ## Resource Types @@ -20,10 +20,6 @@ This module deploys the Managed DevOps Pool resource. | `Microsoft.DevOpsInfrastructure/pools` | [2024-04-04-preview](https://learn.microsoft.com/en-us/azure/templates) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | -## Notes - -The Managed DevOps Pool resource requires external permissions in Azure DevOps. Make sure that the deployment principal has permission in Azure DevOps: [Managed DevOps Pools - Verify Azure DevOps Permissions](https://learn.microsoft.com/en-us/azure/devops/managed-devops-pools/prerequisites?view=azure-devops&tabs=azure-portal#verify-azure-devops-permissions) - ## Usage examples The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. @@ -544,7 +540,6 @@ module pool 'br/public:avm/res/dev-ops-infrastructure/pool:' = {

- ## Parameters **Required parameters** @@ -739,26 +734,24 @@ The type of permission which determines which accounts are admins on the Azure D - Required: No - Type: object -**Optional parameters** +**Required parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`groups`](#parameter-organizationprofilepermissionprofilegroups) | array | Group email addresses. | | [`kind`](#parameter-organizationprofilepermissionprofilekind) | string | Determines who has admin permissions to the Azure DevOps pool. | -| [`users`](#parameter-organizationprofilepermissionprofileusers) | array | User email addresses. | - -### Parameter: `organizationProfile.permissionProfile.groups` -Group email addresses. +**Optional parameters** -- Required: No -- Type: array +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`groups`](#parameter-organizationprofilepermissionprofilegroups) | array | Group email addresses. | +| [`users`](#parameter-organizationprofilepermissionprofileusers) | array | User email addresses. | ### Parameter: `organizationProfile.permissionProfile.kind` Determines who has admin permissions to the Azure DevOps pool. -- Required: No +- Required: Yes - Type: string - Allowed: ```Bicep @@ -769,6 +762,13 @@ Determines who has admin permissions to the Azure DevOps pool. ] ``` +### Parameter: `organizationProfile.permissionProfile.groups` + +Group email addresses. + +- Required: No +- Type: array + ### Parameter: `organizationProfile.permissionProfile.users` User email addresses. @@ -1104,6 +1104,12 @@ Array of role assignments to create. - Required: No - Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` + - `'User Access Administrator'` **Required parameters** @@ -1300,7 +1306,6 @@ Tags of the resource. - Required: No - Type: object - ## Outputs | Output | Type | Description | @@ -1311,9 +1316,9 @@ Tags of the resource. | `resourceId` | string | The resource ID of the Managed DevOps Pool. | | `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | -## Cross-referenced modules +## Notes -_None_ +The Managed DevOps Pool resource requires external permissions in Azure DevOps. Make sure that the deployment principal has permission in Azure DevOps: [Managed DevOps Pools - Verify Azure DevOps Permissions](https://learn.microsoft.com/en-us/azure/devops/managed-devops-pools/prerequisites?view=azure-devops&tabs=azure-portal#verify-azure-devops-permissions) ## Data Collection diff --git a/avm/res/dev-ops-infrastructure/pool/main.bicep b/avm/res/dev-ops-infrastructure/pool/main.bicep index c64d2bbce5..af61e650fe 100644 --- a/avm/res/dev-ops-infrastructure/pool/main.bicep +++ b/avm/res/dev-ops-infrastructure/pool/main.bicep @@ -292,8 +292,8 @@ type organizationProfileType = { @description('Optional. The type of permission which determines which accounts are admins on the Azure DevOps pool.') permissionProfile: { - @description('Optional. Determines who has admin permissions to the Azure DevOps pool.') - kind: ('CreatorOnly' | 'Inherit' | 'SpecificAccounts')? + @description('Required. Determines who has admin permissions to the Azure DevOps pool.') + kind: 'CreatorOnly' | 'Inherit' | 'SpecificAccounts' @description('Optional. Group email addresses.') groups: string[]? diff --git a/avm/res/dev-ops-infrastructure/pool/main.json b/avm/res/dev-ops-infrastructure/pool/main.json index 38edaf771a..ce80f4d180 100644 --- a/avm/res/dev-ops-infrastructure/pool/main.json +++ b/avm/res/dev-ops-infrastructure/pool/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "9138966756251042726" + "version": "0.30.3.12046", + "templateHash": "15191897376801297199" }, "name": "Managed DevOps Pool", "description": "This module deploys the Managed DevOps Pool resource.", @@ -189,9 +189,8 @@ "Inherit", "SpecificAccounts" ], - "nullable": true, "metadata": { - "description": "Optional. Determines who has admin permissions to the Azure DevOps pool." + "description": "Required. Determines who has admin permissions to the Azure DevOps pool." } }, "groups": { From 07a059307535995bdf22a385dfebf224d3ce34ea Mon Sep 17 00:00:00 2001 From: Peter Budai Date: Fri, 20 Sep 2024 09:35:15 +0200 Subject: [PATCH 29/57] feat: Retain existing settings during deployment - `avm/res/web/site` (#3311) ## Description This commit changes the app settings deployment in order to retain existing app settings that are not defined in the Bicep file. This change allows for updating **only** the app settings that are defined in the Bicep file, while leaving the rest unchanged. As this is a change in behavior, version number has been increased. Fixes #949 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.web.site](https://github.com/peterbud/bicep-registry-modules/actions/workflows/avm.res.web.site.yml/badge.svg)](https://github.com/peterbud/bicep-registry-modules/actions/workflows/avm.res.web.site.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [x] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/web/site/README.md | 108 +++++++++++++++--- .../web/site/config--appsettings/README.md | 11 +- .../web/site/config--appsettings/main.bicep | 16 ++- .../web/site/config--appsettings/main.json | 17 ++- avm/res/web/site/main.bicep | 3 +- avm/res/web/site/main.json | 44 ++++--- .../site/slot/config--appsettings/README.md | 9 ++ .../site/slot/config--appsettings/main.bicep | 10 +- .../site/slot/config--appsettings/main.json | 11 +- avm/res/web/site/slot/main.bicep | 1 + avm/res/web/site/slot/main.json | 16 ++- .../functionApp.settings/dependencies.bicep | 21 ++++ .../e2e/functionApp.settings/main.test.bicep | 67 +++++++++++ avm/res/web/site/version.json | 2 +- 14 files changed, 284 insertions(+), 52 deletions(-) create mode 100644 avm/res/web/site/tests/e2e/functionApp.settings/dependencies.bicep create mode 100644 avm/res/web/site/tests/e2e/functionApp.settings/main.test.bicep diff --git a/avm/res/web/site/README.md b/avm/res/web/site/README.md index bc9c390cb6..12c8a2371f 100644 --- a/avm/res/web/site/README.md +++ b/avm/res/web/site/README.md @@ -21,8 +21,9 @@ This module deploys a Web or Function App. | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | | `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | | `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | -| `Microsoft.Web/sites` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/2022-09-01/sites) | +| `Microsoft.Web/sites` | [2023-12-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites) | | `Microsoft.Web/sites/basicPublishingCredentialsPolicies` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites) | +| `Microsoft.Web/sites/config` | [2023-12-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites) | | `Microsoft.Web/sites/config` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites) | | `Microsoft.Web/sites/extensions` | [2023-12-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites/extensions) | | `Microsoft.Web/sites/hybridConnectionNamespaces/relays` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/2022-09-01/sites/hybridConnectionNamespaces/relays) | @@ -41,15 +42,16 @@ The following section provides usage examples for the module, which were used to - [Function App, using only defaults](#example-1-function-app-using-only-defaults) - [Function App, using large parameter set](#example-2-function-app-using-large-parameter-set) -- [Web App, using only defaults](#example-3-web-app-using-only-defaults) -- [Web App](#example-4-web-app) -- [WAF-aligned](#example-5-waf-aligned) -- [Web App, using only defaults](#example-6-web-app-using-only-defaults) -- [Web App, using large parameter set](#example-7-web-app-using-large-parameter-set) -- [Web App, using only defaults](#example-8-web-app-using-only-defaults) -- [Web App, using large parameter set](#example-9-web-app-using-large-parameter-set) -- [Web App](#example-10-web-app) -- [Windows Web App for Containers, using only defaults](#example-11-windows-web-app-for-containers-using-only-defaults) +- [Function App, using only defaults](#example-3-function-app-using-only-defaults) +- [Web App, using only defaults](#example-4-web-app-using-only-defaults) +- [Web App](#example-5-web-app) +- [WAF-aligned](#example-6-waf-aligned) +- [Web App, using only defaults](#example-7-web-app-using-only-defaults) +- [Web App, using large parameter set](#example-8-web-app-using-large-parameter-set) +- [Web App, using only defaults](#example-9-web-app-using-only-defaults) +- [Web App, using large parameter set](#example-10-web-app-using-large-parameter-set) +- [Web App](#example-11-web-app) +- [Windows Web App for Containers, using only defaults](#example-12-windows-web-app-for-containers-using-only-defaults) ### Example 1: _Function App, using only defaults_ @@ -515,7 +517,75 @@ module site 'br/public:avm/res/web/site:' = {

-### Example 3: _Web App, using only defaults_ +### Example 3: _Function App, using only defaults_ + +This instance deploys the module as Function App with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module site 'br/public:avm/res/web/site:' = { + name: 'siteDeployment' + params: { + // Required parameters + kind: 'functionapp' + name: 'wsfaset001' + serverFarmResourceId: '' + // Non-required parameters + appSettingsKeyValuePairs: { + AzureFunctionsJobHost__logging__logLevel__default: 'Trace' + FUNCTIONS_EXTENSION_VERSION: '~4' + FUNCTIONS_WORKER_RUNTIME: 'dotnet' + } + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "kind": { + "value": "functionapp" + }, + "name": { + "value": "wsfaset001" + }, + "serverFarmResourceId": { + "value": "" + }, + // Non-required parameters + "appSettingsKeyValuePairs": { + "value": { + "AzureFunctionsJobHost__logging__logLevel__default": "Trace", + "FUNCTIONS_EXTENSION_VERSION": "~4", + "FUNCTIONS_WORKER_RUNTIME": "dotnet" + } + }, + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 4: _Web App, using only defaults_ This instance deploys the module as a Linux Web App with the minimum set of required parameters. @@ -591,7 +661,7 @@ module site 'br/public:avm/res/web/site:' = {

-### Example 4: _Web App_ +### Example 5: _Web App_ This instance deploys the module as Web App with the set of logs configuration. @@ -723,7 +793,7 @@ module site 'br/public:avm/res/web/site:' = {

-### Example 5: _WAF-aligned_ +### Example 6: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -865,7 +935,7 @@ module site 'br/public:avm/res/web/site:' = {

-### Example 6: _Web App, using only defaults_ +### Example 7: _Web App, using only defaults_ This instance deploys the module as Web App with the minimum set of required parameters. @@ -921,7 +991,7 @@ module site 'br/public:avm/res/web/site:' = {

-### Example 7: _Web App, using large parameter set_ +### Example 8: _Web App, using large parameter set_ This instance deploys the module as Web App with most of its features enabled. @@ -1389,7 +1459,7 @@ module site 'br/public:avm/res/web/site:' = {

-### Example 8: _Web App, using only defaults_ +### Example 9: _Web App, using only defaults_ This instance deploys the module as a Linux Web App with the minimum set of required parameters. @@ -1445,7 +1515,7 @@ module site 'br/public:avm/res/web/site:' = {

-### Example 9: _Web App, using large parameter set_ +### Example 10: _Web App, using large parameter set_ This instance deploys the module asa Linux Web App with most of its features enabled. @@ -1907,7 +1977,7 @@ module site 'br/public:avm/res/web/site:' = {

-### Example 10: _Web App_ +### Example 11: _Web App_ This instance deploys the module as Web App with the set of api management configuration. @@ -2003,7 +2073,7 @@ module site 'br/public:avm/res/web/site:' = {

-### Example 11: _Windows Web App for Containers, using only defaults_ +### Example 12: _Windows Web App for Containers, using only defaults_ This instance deploys the module as a Windows based Container Web App with the minimum set of required parameters. diff --git a/avm/res/web/site/config--appsettings/README.md b/avm/res/web/site/config--appsettings/README.md index 17ff462236..db4a05b7d7 100644 --- a/avm/res/web/site/config--appsettings/README.md +++ b/avm/res/web/site/config--appsettings/README.md @@ -13,7 +13,7 @@ This module deploys a Site App Setting. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.Web/sites/config` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites) | +| `Microsoft.Web/sites/config` | [2023-12-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Web/sites) | ## Parameters @@ -35,6 +35,7 @@ This module deploys a Site App Setting. | :-- | :-- | :-- | | [`appInsightResourceId`](#parameter-appinsightresourceid) | string | Resource ID of the app insight to leverage for this resource. | | [`appSettingsKeyValuePairs`](#parameter-appsettingskeyvaluepairs) | object | The app settings key-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING. | +| [`currentAppSettings`](#parameter-currentappsettings) | object | The current app settings. | | [`storageAccountResourceId`](#parameter-storageaccountresourceid) | string | Required if app of kind functionapp. Resource ID of the storage account to manage triggers and logging function executions. | | [`storageAccountUseIdentityAuthentication`](#parameter-storageaccountuseidentityauthentication) | bool | If the provided storage account requires Identity based authentication ('allowSharedKeyAccess' is set to false). When set to true, the minimum role assignment required for the App Service Managed Identity to the storage account is 'Storage Blob Data Owner'. | @@ -83,6 +84,14 @@ The app settings key-value pairs except for AzureWebJobsStorage, AzureWebJobsDas - Required: No - Type: object +### Parameter: `currentAppSettings` + +The current app settings. + +- Required: No +- Type: object +- Default: `{}` + ### Parameter: `storageAccountResourceId` Required if app of kind functionapp. Resource ID of the storage account to manage triggers and logging function executions. diff --git a/avm/res/web/site/config--appsettings/main.bicep b/avm/res/web/site/config--appsettings/main.bicep index 2f770a7774..202a2ac205 100644 --- a/avm/res/web/site/config--appsettings/main.bicep +++ b/avm/res/web/site/config--appsettings/main.bicep @@ -34,6 +34,9 @@ param appInsightResourceId string? @description('Optional. The app settings key-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING.') param appSettingsKeyValuePairs object? +@description('Optional. The current app settings.') +param currentAppSettings object = {} + var azureWebJobsValues = !empty(storageAccountResourceId) && !(storageAccountUseIdentityAuthentication) ? { AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' @@ -51,9 +54,14 @@ var appInsightsValues = !empty(appInsightResourceId) } : {} -var expandedAppSettings = union(appSettingsKeyValuePairs ?? {}, azureWebJobsValues, appInsightsValues) +var expandedAppSettings = union( + currentAppSettings ?? {}, + appSettingsKeyValuePairs ?? {}, + azureWebJobsValues, + appInsightsValues +) -resource app 'Microsoft.Web/sites@2022-09-01' existing = { +resource app 'Microsoft.Web/sites@2023-12-01' existing = { name: appName } @@ -62,7 +70,7 @@ resource appInsight 'Microsoft.Insights/components@2020-02-02' existing = if (!e scope: resourceGroup(split(appInsightResourceId ?? '//', '/')[2], split(appInsightResourceId ?? '////', '/')[4]) } -resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' existing = if (!empty(storageAccountResourceId)) { +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' existing = if (!empty(storageAccountResourceId)) { name: last(split(storageAccountResourceId ?? 'dummyName', '/')) scope: resourceGroup( split(storageAccountResourceId ?? '//', '/')[2], @@ -70,7 +78,7 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' existing ) } -resource appSettings 'Microsoft.Web/sites/config@2022-09-01' = { +resource appSettings 'Microsoft.Web/sites/config@2023-12-01' = { name: 'appsettings' kind: kind parent: app diff --git a/avm/res/web/site/config--appsettings/main.json b/avm/res/web/site/config--appsettings/main.json index c59a554e2a..c50105f501 100644 --- a/avm/res/web/site/config--appsettings/main.json +++ b/avm/res/web/site/config--appsettings/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "8777070640548664577" + "templateHash": "3998275265127709875" }, "name": "Site App Settings", "description": "This module deploys a Site App Setting.", @@ -66,13 +66,20 @@ "metadata": { "description": "Optional. The app settings key-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING." } + }, + "currentAppSettings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The current app settings." + } } }, "resources": { "app": { "existing": true, "type": "Microsoft.Web/sites", - "apiVersion": "2022-09-01", + "apiVersion": "2023-12-01", "name": "[parameters('appName')]" }, "appInsight": { @@ -88,17 +95,17 @@ "condition": "[not(empty(parameters('storageAccountResourceId')))]", "existing": true, "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2023-01-01", + "apiVersion": "2023-05-01", "subscriptionId": "[split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2]]", "resourceGroup": "[split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]]", "name": "[last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))]" }, "appSettings": { "type": "Microsoft.Web/sites/config", - "apiVersion": "2022-09-01", + "apiVersion": "2023-12-01", "name": "[format('{0}/{1}', parameters('appName'), 'appsettings')]", "kind": "[parameters('kind')]", - "properties": "[union(coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", + "properties": "[union(coalesce(parameters('currentAppSettings'), createObject()), coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-05-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", "dependsOn": [ "app", "appInsight", diff --git a/avm/res/web/site/main.bicep b/avm/res/web/site/main.bicep index b7418015b6..cbd5824ec5 100644 --- a/avm/res/web/site/main.bicep +++ b/avm/res/web/site/main.bicep @@ -245,7 +245,7 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableT } } -resource app 'Microsoft.Web/sites@2022-09-01' = { +resource app 'Microsoft.Web/sites@2023-12-01' = { name: name location: location kind: kind @@ -294,6 +294,7 @@ module app_appsettings 'config--appsettings/main.bicep' = if (!empty(appSettings storageAccountUseIdentityAuthentication: storageAccountUseIdentityAuthentication appInsightResourceId: appInsightResourceId appSettingsKeyValuePairs: appSettingsKeyValuePairs + currentAppSettings: !empty(app.id) ? list('${app.id}/config/appsettings', '2023-12-01').properties : {} } } diff --git a/avm/res/web/site/main.json b/avm/res/web/site/main.json index e6eccb3688..d3577e551f 100644 --- a/avm/res/web/site/main.json +++ b/avm/res/web/site/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "7320044434284742277" + "templateHash": "4626438010490721609" }, "name": "Web/Function Apps", "description": "This module deploys a Web or Function App.", @@ -860,7 +860,7 @@ }, "app": { "type": "Microsoft.Web/sites", - "apiVersion": "2022-09-01", + "apiVersion": "2023-12-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "kind": "[parameters('kind')]", @@ -998,7 +998,8 @@ }, "appSettingsKeyValuePairs": { "value": "[parameters('appSettingsKeyValuePairs')]" - } + }, + "currentAppSettings": "[if(not(empty(resourceId('Microsoft.Web/sites', parameters('name')))), createObject('value', list(format('{0}/config/appsettings', resourceId('Microsoft.Web/sites', parameters('name'))), '2023-12-01').properties), createObject('value', createObject()))]" }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -1008,7 +1009,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "8777070640548664577" + "templateHash": "3998275265127709875" }, "name": "Site App Settings", "description": "This module deploys a Site App Setting.", @@ -1068,13 +1069,20 @@ "metadata": { "description": "Optional. The app settings key-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING." } + }, + "currentAppSettings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The current app settings." + } } }, "resources": { "app": { "existing": true, "type": "Microsoft.Web/sites", - "apiVersion": "2022-09-01", + "apiVersion": "2023-12-01", "name": "[parameters('appName')]" }, "appInsight": { @@ -1090,17 +1098,17 @@ "condition": "[not(empty(parameters('storageAccountResourceId')))]", "existing": true, "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2023-01-01", + "apiVersion": "2023-05-01", "subscriptionId": "[split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2]]", "resourceGroup": "[split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]]", "name": "[last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))]" }, "appSettings": { "type": "Microsoft.Web/sites/config", - "apiVersion": "2022-09-01", + "apiVersion": "2023-12-01", "name": "[format('{0}/{1}', parameters('appName'), 'appsettings')]", "kind": "[parameters('kind')]", - "properties": "[union(coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", + "properties": "[union(coalesce(parameters('currentAppSettings'), createObject()), coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-05-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", "dependsOn": [ "app", "appInsight", @@ -1677,7 +1685,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "15729572124587777376" + "templateHash": "13282951347078727812" }, "name": "Web/Function App Deployment Slots", "description": "This module deploys a Web or Function App Deployment Slot.", @@ -2639,7 +2647,8 @@ }, "appSettingsKeyValuePairs": { "value": "[parameters('appSettingsKeyValuePairs')]" - } + }, + "currentAppSettings": "[if(not(empty(resourceId('Microsoft.Web/sites/slots', parameters('appName'), parameters('name')))), createObject('value', list(format('{0}/config/appsettings', resourceId('Microsoft.Web/sites/slots', parameters('appName'), parameters('name'))), '2023-12-01').properties), createObject('value', createObject()))]" }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -2649,7 +2658,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "7111332561212908044" + "templateHash": "9363357518124041583" }, "name": "Site Slot App Settings", "description": "This module deploys a Site Slot App Setting.", @@ -2715,6 +2724,13 @@ "metadata": { "description": "Optional. The app settings key-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING." } + }, + "currentAppSettings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The current app settings." + } } }, "resources": { @@ -2756,7 +2772,7 @@ "apiVersion": "2022-09-01", "name": "[format('{0}/{1}/{2}', parameters('appName'), parameters('slotName'), 'appsettings')]", "kind": "[parameters('kind')]", - "properties": "[union(coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", + "properties": "[union(coalesce(parameters('currentAppSettings'), createObject()), coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", "dependsOn": [ "appInsight", "app::slot", @@ -5111,7 +5127,7 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[coalesce(tryGet(tryGet(reference('app', '2022-09-01', 'full'), 'identity'), 'principalId'), '')]" + "value": "[coalesce(tryGet(tryGet(reference('app', '2023-12-01', 'full'), 'identity'), 'principalId'), '')]" }, "slotSystemAssignedMIPrincipalIds": { "type": "array", @@ -5128,7 +5144,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('app', '2022-09-01', 'full').location]" + "value": "[reference('app', '2023-12-01', 'full').location]" }, "defaultHostname": { "type": "string", diff --git a/avm/res/web/site/slot/config--appsettings/README.md b/avm/res/web/site/slot/config--appsettings/README.md index 6be3108fe1..a4eaf7b5e3 100644 --- a/avm/res/web/site/slot/config--appsettings/README.md +++ b/avm/res/web/site/slot/config--appsettings/README.md @@ -36,6 +36,7 @@ This module deploys a Site Slot App Setting. | :-- | :-- | :-- | | [`appInsightResourceId`](#parameter-appinsightresourceid) | string | Resource ID of the app insight to leverage for this resource. | | [`appSettingsKeyValuePairs`](#parameter-appsettingskeyvaluepairs) | object | The app settings key-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING. | +| [`currentAppSettings`](#parameter-currentappsettings) | object | The current app settings. | | [`storageAccountResourceId`](#parameter-storageaccountresourceid) | string | Required if app of kind functionapp. Resource ID of the storage account to manage triggers and logging function executions. | | [`storageAccountUseIdentityAuthentication`](#parameter-storageaccountuseidentityauthentication) | bool | If the provided storage account requires Identity based authentication ('allowSharedKeyAccess' is set to false). When set to true, the minimum role assignment required for the App Service Managed Identity to the storage account is 'Storage Blob Data Owner'. | @@ -91,6 +92,14 @@ The app settings key-value pairs except for AzureWebJobsStorage, AzureWebJobsDas - Required: No - Type: object +### Parameter: `currentAppSettings` + +The current app settings. + +- Required: No +- Type: object +- Default: `{}` + ### Parameter: `storageAccountResourceId` Required if app of kind functionapp. Resource ID of the storage account to manage triggers and logging function executions. diff --git a/avm/res/web/site/slot/config--appsettings/main.bicep b/avm/res/web/site/slot/config--appsettings/main.bicep index 74095161a8..2fbe7b81d4 100644 --- a/avm/res/web/site/slot/config--appsettings/main.bicep +++ b/avm/res/web/site/slot/config--appsettings/main.bicep @@ -37,6 +37,9 @@ param appInsightResourceId string? @description('Optional. The app settings key-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING.') param appSettingsKeyValuePairs object? +@description('Optional. The current app settings.') +param currentAppSettings object = {} + var azureWebJobsValues = !empty(storageAccountResourceId) && !(storageAccountUseIdentityAuthentication) ? { AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${storageAccount.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' @@ -54,7 +57,12 @@ var appInsightsValues = !empty(appInsightResourceId) } : {} -var expandedAppSettings = union(appSettingsKeyValuePairs ?? {}, azureWebJobsValues, appInsightsValues) +var expandedAppSettings = union( + currentAppSettings ?? {}, + appSettingsKeyValuePairs ?? {}, + azureWebJobsValues, + appInsightsValues +) resource app 'Microsoft.Web/sites@2022-09-01' existing = { name: appName diff --git a/avm/res/web/site/slot/config--appsettings/main.json b/avm/res/web/site/slot/config--appsettings/main.json index 48ed22304b..af2de6024a 100644 --- a/avm/res/web/site/slot/config--appsettings/main.json +++ b/avm/res/web/site/slot/config--appsettings/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "7111332561212908044" + "templateHash": "9363357518124041583" }, "name": "Site Slot App Settings", "description": "This module deploys a Site Slot App Setting.", @@ -72,6 +72,13 @@ "metadata": { "description": "Optional. The app settings key-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING." } + }, + "currentAppSettings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The current app settings." + } } }, "resources": { @@ -113,7 +120,7 @@ "apiVersion": "2022-09-01", "name": "[format('{0}/{1}/{2}', parameters('appName'), parameters('slotName'), 'appsettings')]", "kind": "[parameters('kind')]", - "properties": "[union(coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", + "properties": "[union(coalesce(parameters('currentAppSettings'), createObject()), coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", "dependsOn": [ "appInsight", "app::slot", diff --git a/avm/res/web/site/slot/main.bicep b/avm/res/web/site/slot/main.bicep index b871bf0960..95b2e0eff7 100644 --- a/avm/res/web/site/slot/main.bicep +++ b/avm/res/web/site/slot/main.bicep @@ -264,6 +264,7 @@ module slot_appsettings 'config--appsettings/main.bicep' = if (!empty(appSetting storageAccountUseIdentityAuthentication: storageAccountUseIdentityAuthentication appInsightResourceId: appInsightResourceId appSettingsKeyValuePairs: appSettingsKeyValuePairs + currentAppSettings: !empty(slot.id) ? list('${slot.id}/config/appsettings', '2023-12-01').properties : {} } } diff --git a/avm/res/web/site/slot/main.json b/avm/res/web/site/slot/main.json index 8f8f81e34a..3120546cad 100644 --- a/avm/res/web/site/slot/main.json +++ b/avm/res/web/site/slot/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "15729572124587777376" + "templateHash": "13282951347078727812" }, "name": "Web/Function App Deployment Slots", "description": "This module deploys a Web or Function App Deployment Slot.", @@ -968,7 +968,8 @@ }, "appSettingsKeyValuePairs": { "value": "[parameters('appSettingsKeyValuePairs')]" - } + }, + "currentAppSettings": "[if(not(empty(resourceId('Microsoft.Web/sites/slots', parameters('appName'), parameters('name')))), createObject('value', list(format('{0}/config/appsettings', resourceId('Microsoft.Web/sites/slots', parameters('appName'), parameters('name'))), '2023-12-01').properties), createObject('value', createObject()))]" }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -978,7 +979,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "7111332561212908044" + "templateHash": "9363357518124041583" }, "name": "Site Slot App Settings", "description": "This module deploys a Site Slot App Setting.", @@ -1044,6 +1045,13 @@ "metadata": { "description": "Optional. The app settings key-value pairs except for AzureWebJobsStorage, AzureWebJobsDashboard, APPINSIGHTS_INSTRUMENTATIONKEY and APPLICATIONINSIGHTS_CONNECTION_STRING." } + }, + "currentAppSettings": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. The current app settings." + } } }, "resources": { @@ -1085,7 +1093,7 @@ "apiVersion": "2022-09-01", "name": "[format('{0}/{1}/{2}', parameters('appName'), parameters('slotName'), 'appsettings')]", "kind": "[parameters('kind')]", - "properties": "[union(coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", + "properties": "[union(coalesce(parameters('currentAppSettings'), createObject()), coalesce(parameters('appSettingsKeyValuePairs'), createObject()), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/')), listKeys(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(coalesce(parameters('storageAccountResourceId'), '//'), '/')[2], split(coalesce(parameters('storageAccountResourceId'), '////'), '/')[4]), 'Microsoft.Storage/storageAccounts', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), '2023-01-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), union(createObject('AzureWebJobsStorage__accountName', last(split(coalesce(parameters('storageAccountResourceId'), 'dummyName'), '/'))), createObject('AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob)), createObject())), if(not(empty(parameters('appInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('appInsight').ConnectionString), createObject()))]", "dependsOn": [ "appInsight", "app::slot", diff --git a/avm/res/web/site/tests/e2e/functionApp.settings/dependencies.bicep b/avm/res/web/site/tests/e2e/functionApp.settings/dependencies.bicep new file mode 100644 index 0000000000..dd34e10b1c --- /dev/null +++ b/avm/res/web/site/tests/e2e/functionApp.settings/dependencies.bicep @@ -0,0 +1,21 @@ +@description('Optional. The location to deploy resources to.') +param location string = resourceGroup().location + +@description('Required. The name of the Server Farm to create.') +param serverFarmName string + +resource serverFarm 'Microsoft.Web/serverfarms@2022-03-01' = { + name: serverFarmName + location: location + sku: { + name: 'S1' + tier: 'Standard' + size: 'S1' + family: 'S' + capacity: 1 + } + properties: {} +} + +@description('The resource ID of the created Server Farm.') +output serverFarmResourceId string = serverFarm.id diff --git a/avm/res/web/site/tests/e2e/functionApp.settings/main.test.bicep b/avm/res/web/site/tests/e2e/functionApp.settings/main.test.bicep new file mode 100644 index 0000000000..a62f7217a1 --- /dev/null +++ b/avm/res/web/site/tests/e2e/functionApp.settings/main.test.bicep @@ -0,0 +1,67 @@ +targetScope = 'subscription' + +metadata name = 'Function App, using only defaults' +metadata description = 'This instance deploys the module as Function App with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-web.sites-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'wsfaset' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + serverFarmName: 'dep-${namePrefix}-sf-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + kind: 'functionapp' + serverFarmResourceId: nestedDependencies.outputs.serverFarmResourceId + appSettingsKeyValuePairs: { + AzureFunctionsJobHost__logging__logLevel__default: 'Trace' + FUNCTIONS_EXTENSION_VERSION: '~4' + FUNCTIONS_WORKER_RUNTIME: 'dotnet' + } + } + dependsOn: [ + nestedDependencies + ] + } +] diff --git a/avm/res/web/site/version.json b/avm/res/web/site/version.json index 0f81d22abc..b8b30a0125 100644 --- a/avm/res/web/site/version.json +++ b/avm/res/web/site/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.8", + "version": "0.9", "pathFilters": [ "./main.json" ] From 73fc84f9a61c1427b1f4947805d7d41cdaaacb74 Mon Sep 17 00:00:00 2001 From: Fabio Masciotra Date: Mon, 23 Sep 2024 13:35:56 +0200 Subject: [PATCH 30/57] fix: module `avm/res/network/nat-gateway` (#3341) ## Description added a test case about the use of an existing Public IP address Closes #2378 ## Pipeline Reference [![avm.res.network.nat-gateway](https://github.com/fabmas/bicep-registry-modules/actions/workflows/avm.res.network.nat-gateway.yml/badge.svg?branch=natgw)](https://github.com/fabmas/bicep-registry-modules/actions/workflows/avm.res.network.nat-gateway.yml) | Pipeline | | -------- | | | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/network/nat-gateway/README.md | 69 +++++++++++++++++-- avm/res/network/nat-gateway/main.bicep | 4 +- avm/res/network/nat-gateway/main.json | 20 +++--- .../tests/e2e/existingPip/dependencies.bicep | 25 +++++++ .../tests/e2e/existingPip/main.test.bicep | 61 ++++++++++++++++ 5 files changed, 163 insertions(+), 16 deletions(-) create mode 100644 avm/res/network/nat-gateway/tests/e2e/existingPip/dependencies.bicep create mode 100644 avm/res/network/nat-gateway/tests/e2e/existingPip/main.test.bicep diff --git a/avm/res/network/nat-gateway/README.md b/avm/res/network/nat-gateway/README.md index 8c6358ff0c..d12c7b26f7 100644 --- a/avm/res/network/nat-gateway/README.md +++ b/avm/res/network/nat-gateway/README.md @@ -31,9 +31,10 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br/public:avm/res/network/nat-gateway:`. - [Using only defaults](#example-1-using-only-defaults) -- [Using large parameter set](#example-2-using-large-parameter-set) -- [Combine a generated and provided Public IP Prefix](#example-3-combine-a-generated-and-provided-public-ip-prefix) -- [WAF-aligned](#example-4-waf-aligned) +- [Using an existing Public IP](#example-2-using-an-existing-public-ip) +- [Using large parameter set](#example-3-using-large-parameter-set) +- [Combine a generated and provided Public IP Prefix](#example-4-combine-a-generated-and-provided-public-ip-prefix) +- [WAF-aligned](#example-5-waf-aligned) ### Example 1: _Using only defaults_ @@ -87,7 +88,63 @@ module natGateway 'br/public:avm/res/network/nat-gateway:' = {

-### Example 2: _Using large parameter set_ +### Example 2: _Using an existing Public IP_ + +This instance deploys the module using an existing Public IP address. + + +

+ +via Bicep module + +```bicep +module natGateway 'br/public:avm/res/network/nat-gateway:' = { + name: 'natGatewayDeployment' + params: { + // Required parameters + name: 'nngepip001' + zone: 1 + // Non-required parameters + location: '' + publicIpResourceIds: '' + } +} +``` + +
+

+ +

+ +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": "nngepip001" + }, + "zone": { + "value": 1 + }, + // Non-required parameters + "location": { + "value": "" + }, + "publicIpResourceIds": { + "value": "" + } + } +} +``` + +
+

+ +### Example 3: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -287,7 +344,7 @@ module natGateway 'br/public:avm/res/network/nat-gateway:' = {

-### Example 3: _Combine a generated and provided Public IP Prefix_ +### Example 4: _Combine a generated and provided Public IP Prefix_ This example shows how you can provide a Public IP Prefix to the module, while also generating one in the module. @@ -359,7 +416,7 @@ module natGateway 'br/public:avm/res/network/nat-gateway:' = {

-### Example 4: _WAF-aligned_ +### Example 5: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. diff --git a/avm/res/network/nat-gateway/main.bicep b/avm/res/network/nat-gateway/main.bicep index 8836c815fe..f576caedd5 100644 --- a/avm/res/network/nat-gateway/main.bicep +++ b/avm/res/network/nat-gateway/main.bicep @@ -99,7 +99,7 @@ module publicIPAddresses 'br/public:avm/res/network/public-ip-address:0.5.1' = [ for (publicIPAddressObject, index) in (publicIPAddressObjects ?? []): { name: '${uniqueString(deployment().name, location)}-NatGw-PIP-${index}' params: { - name: contains(publicIPAddressObject, 'name') ? publicIPAddressObject.name : '${name}-pip' + name: publicIPAddressObject.?name ?? '${name}-pip' location: location lock: publicIPAddressObject.?lock ?? lock diagnosticSettings: publicIPAddressObject.?diagnosticSettings @@ -133,7 +133,7 @@ module publicIPPrefixes 'br/public:avm/res/network/public-ip-prefix:0.4.1' = [ for (publicIPPrefixObject, index) in (publicIPPrefixObjects ?? []): { name: '${uniqueString(deployment().name, location)}-NatGw-Prefix-PIP-${index}' params: { - name: contains(publicIPPrefixObject, 'name') ? publicIPPrefixObject.name : '${name}-pip' + name: publicIPPrefixObject.?name ?? '${name}-pip' location: location lock: publicIPPrefixObject.?lock ?? lock prefixLength: publicIPPrefixObject.prefixLength diff --git a/avm/res/network/nat-gateway/main.json b/avm/res/network/nat-gateway/main.json index 414a6e0129..588a626b30 100644 --- a/avm/res/network/nat-gateway/main.json +++ b/avm/res/network/nat-gateway/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3430947452943320440" + "version": "0.30.3.12046", + "templateHash": "16462612640291787003" }, "name": "NAT Gateways", "description": "This module deploys a NAT Gateway.", @@ -308,7 +308,9 @@ }, "mode": "Incremental", "parameters": { - "name": "[if(contains(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'name'), createObject('value', coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()].name), createObject('value', format('{0}-pip', parameters('name'))))]", + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('publicIPAddressObjects'), createArray())[copyIndex()], 'name'), format('{0}-pip', parameters('name')))]" + }, "location": { "value": "[parameters('location')]" }, @@ -1004,8 +1006,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15040145942768763519" + "version": "0.30.3.12046", + "templateHash": "9121047532434826411" } }, "parameters": { @@ -1050,7 +1052,9 @@ }, "mode": "Incremental", "parameters": { - "name": "[if(contains(coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()], 'name'), createObject('value', coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()].name), createObject('value', format('{0}-pip', parameters('name'))))]", + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('publicIPPrefixObjects'), createArray())[copyIndex()], 'name'), format('{0}-pip', parameters('name')))]" + }, "location": { "value": "[parameters('location')]" }, @@ -1416,8 +1420,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15040145942768763519" + "version": "0.30.3.12046", + "templateHash": "9121047532434826411" } }, "parameters": { diff --git a/avm/res/network/nat-gateway/tests/e2e/existingPip/dependencies.bicep b/avm/res/network/nat-gateway/tests/e2e/existingPip/dependencies.bicep new file mode 100644 index 0000000000..d12b008b0c --- /dev/null +++ b/avm/res/network/nat-gateway/tests/e2e/existingPip/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 existingPipName string + +resource existingPip 'Microsoft.Network/publicIPAddresses@2023-04-01' = { + name: existingPipName + location: location + sku: { + name: 'Standard' + tier: 'Regional' + } + properties: { + publicIPAllocationMethod: 'Static' + } + zones: [ + '1' + '2' + '3' + ] +} + +@description('The resource ID of the existing Public IP.') +output existingPipResourceId string = existingPip.id diff --git a/avm/res/network/nat-gateway/tests/e2e/existingPip/main.test.bicep b/avm/res/network/nat-gateway/tests/e2e/existingPip/main.test.bicep new file mode 100644 index 0000000000..1b13848208 --- /dev/null +++ b/avm/res/network/nat-gateway/tests/e2e/existingPip/main.test.bicep @@ -0,0 +1,61 @@ +targetScope = 'subscription' + +metadata name = 'Using an existing Public IP' +metadata description = 'This instance deploys the module using an existing Public IP address.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.natgateway-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nngepip' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + existingPipName: '${namePrefix}${serviceShort}001-existingpip1' + + } +} + + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + location: resourceLocation + name: '${namePrefix}${serviceShort}001' + zone: 1 + publicIpResourceIds: [nestedDependencies.outputs.existingPipResourceId] + } + } +] From 201c378a69ca7cf115b71a900091e708ba00c909 Mon Sep 17 00:00:00 2001 From: Chinedum Echeta <60179183+cecheta@users.noreply.github.com> Date: Tue, 24 Sep 2024 00:34:43 -0700 Subject: [PATCH 31/57] feat: `avm/ptn/ai-platform/baseline` Change identities of AI workspaces (#3345) ## Description This PR changes the managed identities of both the workspace project and hub * The workspace hub no longer creates a user assigned identity, but instead expects the name of an already existing UAI to be passed in. This is because there is now validation when creating a workspace that it should have permissions to approve private endpoint connections * The workspace project now only uses a system assigned identity, as it is not possible to authenticate with a private container registry using a user assigned identity ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.ai-platform.baseline](https://github.com/cecheta/bicep-registry-modules/actions/workflows/avm.ptn.ai-platform.baseline.yml/badge.svg?branch=identity)](https://github.com/cecheta/bicep-registry-modules/actions/workflows/avm.ptn.ai-platform.baseline.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [x] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/ptn/ai-platform/baseline/README.md | 56 +--- avm/ptn/ai-platform/baseline/main.bicep | 286 ++++++---------- avm/ptn/ai-platform/baseline/main.json | 309 +++--------------- .../baseline/tests/e2e/max/dependencies.bicep | 11 + .../baseline/tests/e2e/max/main.test.bicep | 6 +- .../tests/e2e/waf-aligned/dependencies.bicep | 23 ++ .../tests/e2e/waf-aligned/main.test.bicep | 2 + avm/ptn/ai-platform/baseline/version.json | 2 +- 8 files changed, 200 insertions(+), 495 deletions(-) diff --git a/avm/ptn/ai-platform/baseline/README.md b/avm/ptn/ai-platform/baseline/README.md index 45718c8a77..dfaf48f4a5 100644 --- a/avm/ptn/ai-platform/baseline/README.md +++ b/avm/ptn/ai-platform/baseline/README.md @@ -38,7 +38,6 @@ By integrating with Microsoft Entra ID for secure identity management and utiliz | `Microsoft.MachineLearningServices/workspaces` | [2024-04-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.MachineLearningServices/2024-04-01-preview/workspaces) | | `Microsoft.MachineLearningServices/workspaces/computes` | [2022-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.MachineLearningServices/2022-10-01/workspaces/computes) | | `Microsoft.Maintenance/configurationAssignments` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Maintenance/2023-04-01/configurationAssignments) | -| `Microsoft.ManagedIdentity/userAssignedIdentities` | [2023-01-31](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ManagedIdentity/2023-01-31/userAssignedIdentities) | | `Microsoft.Network/bastionHosts` | [2022-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2022-11-01/bastionHosts) | | `Microsoft.Network/networkInterfaces` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/networkInterfaces) | | `Microsoft.Network/networkSecurityGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/networkSecurityGroups) | @@ -54,8 +53,8 @@ By integrating with Microsoft Entra ID for secure identity management and utiliz | `Microsoft.Network/privateDnsZones/virtualNetworkLinks` | [2020-06-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2020-06-01/privateDnsZones/virtualNetworkLinks) | | `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | | `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Network/publicIPAddresses` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-09-01/publicIPAddresses) | | `Microsoft.Network/virtualNetworks` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks) | | `Microsoft.Network/virtualNetworks/subnets` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks/subnets) | @@ -185,10 +184,7 @@ module baseline 'br/public:avm/ptn/ai-platform/baseline:' = { logAnalyticsConfiguration: { name: 'log-aipbmax' } - managedIdentityConfiguration: { - hubName: 'id-hub-aipbmax' - projectName: 'id-project-aipbmax' - } + managedIdentityName: '' storageAccountConfiguration: { allowSharedKeyAccess: true name: 'staipbmax' @@ -321,11 +317,8 @@ module baseline 'br/public:avm/ptn/ai-platform/baseline:' = { "name": "log-aipbmax" } }, - "managedIdentityConfiguration": { - "value": { - "hubName": "id-hub-aipbmax", - "projectName": "id-project-aipbmax" - } + "managedIdentityName": { + "value": "" }, "storageAccountConfiguration": { "value": { @@ -544,6 +537,7 @@ module baseline 'br/public:avm/ptn/ai-platform/baseline:' = { // Required parameters name: '' // Non-required parameters + managedIdentityName: '' tags: { Env: 'test' 'hidden-title': 'This is visible in the resource name' @@ -591,6 +585,9 @@ module baseline 'br/public:avm/ptn/ai-platform/baseline:' = { "value": "" }, // Non-required parameters + "managedIdentityName": { + "value": "" + }, "tags": { "value": { "Env": "test", @@ -649,7 +646,7 @@ module baseline 'br/public:avm/ptn/ai-platform/baseline:' = { | [`keyVaultConfiguration`](#parameter-keyvaultconfiguration) | object | Configuration for the key vault. | | [`location`](#parameter-location) | string | Location for all Resources. | | [`logAnalyticsConfiguration`](#parameter-loganalyticsconfiguration) | object | Configuration for the Log Analytics workspace. | -| [`managedIdentityConfiguration`](#parameter-managedidentityconfiguration) | object | Configuration for the user-assigned managed identities. | +| [`managedIdentityName`](#parameter-managedidentityname) | string | The name of the user assigned identity for the AI Studio hub. If not provided, the hub will use a system assigned identity. | | [`storageAccountConfiguration`](#parameter-storageaccountconfiguration) | object | Configuration for the storage account. | | [`tags`](#parameter-tags) | object | Resource tags. | | [`virtualMachineConfiguration`](#parameter-virtualmachineconfiguration) | secureObject | Configuration for the virtual machine. | @@ -889,30 +886,9 @@ The name of the Log Analytics workspace. - Required: No - Type: string -### Parameter: `managedIdentityConfiguration` - -Configuration for the user-assigned managed identities. - -- Required: No -- Type: object - -**Optional parameters** - -| Parameter | Type | Description | -| :-- | :-- | :-- | -| [`hubName`](#parameter-managedidentityconfigurationhubname) | string | The name of the workspace hub user-assigned managed identity. | -| [`projectName`](#parameter-managedidentityconfigurationprojectname) | string | The name of the workspace project user-assigned managed identity. | - -### Parameter: `managedIdentityConfiguration.hubName` - -The name of the workspace hub user-assigned managed identity. - -- Required: No -- Type: string - -### Parameter: `managedIdentityConfiguration.projectName` +### Parameter: `managedIdentityName` -The name of the workspace project user-assigned managed identity. +The name of the user assigned identity for the AI Studio hub. If not provided, the hub will use a system assigned identity. - Required: No - Type: string @@ -1144,14 +1120,6 @@ The name of the AI Studio workspace project. | `location` | string | The location the module was deployed to. | | `logAnalyticsWorkspaceName` | string | The name of the log analytics workspace. | | `logAnalyticsWorkspaceResourceId` | string | The resource ID of the log analytics workspace. | -| `managedIdentityHubClientId` | string | The client ID of the workspace hub user assigned managed identity. | -| `managedIdentityHubName` | string | The name of the workspace hub user assigned managed identity. | -| `managedIdentityHubPrincipalId` | string | The principal ID of the workspace hub user assigned managed identity. | -| `managedIdentityHubResourceId` | string | The resource ID of the workspace hub user assigned managed identity. | -| `managedIdentityProjectClientId` | string | The client ID of the workspace project user assigned managed identity. | -| `managedIdentityProjectName` | string | The name of the workspace project user assigned managed identity. | -| `managedIdentityProjectPrincipalId` | string | The principal ID of the workspace project user assigned managed identity. | -| `managedIdentityProjectResourceId` | string | The resource ID of the workspace project user assigned managed identity. | | `resourceGroupName` | string | The name of the resource group the module was deployed to. | | `storageAccountName` | string | The name of the storage account. | | `storageAccountResourceId` | string | The resource ID of the storage account. | @@ -1161,8 +1129,10 @@ The name of the AI Studio workspace project. | `virtualNetworkResourceId` | string | The resource ID of the virtual network. | | `virtualNetworkSubnetName` | string | The name of the subnet in the virtual network. | | `virtualNetworkSubnetResourceId` | string | The resource ID of the subnet in the virtual network. | +| `workspaceHubManagedIdentityPrincipalId` | string | The principal ID of the workspace hub system assigned identity, if applicable. | | `workspaceHubName` | string | The name of the workspace hub. | | `workspaceHubResourceId` | string | The resource ID of the workspace hub. | +| `workspaceProjectManagedIdentityPrincipalId` | string | The principal ID of the workspace project system assigned identity. | | `workspaceProjectName` | string | The name of the workspace project. | | `workspaceProjectResourceId` | string | The resource ID of the workspace project. | diff --git a/avm/ptn/ai-platform/baseline/main.bicep b/avm/ptn/ai-platform/baseline/main.bicep index d3095c0f79..29b505f083 100644 --- a/avm/ptn/ai-platform/baseline/main.bicep +++ b/avm/ptn/ai-platform/baseline/main.bicep @@ -17,8 +17,8 @@ param tags object? @description('Optional. Enable/Disable usage telemetry for module.') param enableTelemetry bool = true -@description('Optional. Configuration for the user-assigned managed identities.') -param managedIdentityConfiguration managedIdentityConfigurationType +@description('Optional. The name of the user assigned identity for the AI Studio hub. If not provided, the hub will use a system assigned identity.') +param managedIdentityName string? @description('Optional. Configuration for the Log Analytics workspace.') param logAnalyticsConfiguration logAnalyticsConfigurationType @@ -120,13 +120,15 @@ module workspaceHub_privateDnsZones 'br/public:avm/res/network/private-dns-zone: virtualNetworkResourceId: virtualNetwork.id } ] - roleAssignments: [ - { - principalId: managedIdentityHub.properties.principalId - roleDefinitionIdOrName: 'Contributor' - principalType: 'ServicePrincipal' - } - ] + roleAssignments: managedIdentityName != null + ? [ + { + principalId: userAssignedIdentity.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + ] + : null } } ] @@ -278,16 +280,8 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.5.3' = if (cr } } -resource managedIdentityHub 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { - name: managedIdentityConfiguration.?hubName ?? 'id-hub-${name}' - location: location - tags: tags -} - -resource managedIdentityProject 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { - name: managedIdentityConfiguration.?projectName ?? 'id-project-${name}' - location: location - tags: tags +resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (managedIdentityName != null) { + name: managedIdentityName ?? 'null' } resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09-01' = { @@ -296,14 +290,14 @@ resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2023-09 tags: tags } -resource resourceGroup_roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { +resource resourceGroup_roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = if (managedIdentityName != null) { name: guid(resourceGroup().id, name) properties: { roleDefinitionId: subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7' // Reader ) - principalId: managedIdentityHub.properties.principalId + principalId: userAssignedIdentity.properties.principalId principalType: 'ServicePrincipal' } } @@ -324,28 +318,20 @@ module keyVault 'br/public:avm/res/key-vault/vault:0.6.2' = { } publicNetworkAccess: 'Disabled' enablePurgeProtection: keyVaultConfiguration.?enablePurgeProtection ?? true - roleAssignments: [ - { - principalId: managedIdentityHub.properties.principalId - roleDefinitionIdOrName: 'Contributor' - principalType: 'ServicePrincipal' - } - { - principalId: managedIdentityProject.properties.principalId - roleDefinitionIdOrName: 'Contributor' - principalType: 'ServicePrincipal' - } - { - principalId: managedIdentityHub.properties.principalId - roleDefinitionIdOrName: 'Key Vault Administrator' - principalType: 'ServicePrincipal' - } - { - principalId: managedIdentityProject.properties.principalId - roleDefinitionIdOrName: 'Key Vault Administrator' - principalType: 'ServicePrincipal' - } - ] + roleAssignments: managedIdentityName != null + ? [ + { + principalId: userAssignedIdentity.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + { + principalId: userAssignedIdentity.properties.principalId + roleDefinitionIdOrName: 'Key Vault Administrator' + principalType: 'ServicePrincipal' + } + ] + : null diagnosticSettings: [ { workspaceResourceId: logAnalyticsWorkspace.id @@ -385,48 +371,25 @@ module storageAccount 'br/public:avm/res/storage/storage-account:0.11.0' = { privateDnsZoneResourceIds: [resourceId('Microsoft.Network/privateDnsZones', zone.key)] }) : null - roleAssignments: [ - { - principalId: managedIdentityHub.properties.principalId - roleDefinitionIdOrName: 'Contributor' - principalType: 'ServicePrincipal' - } - { - principalId: managedIdentityProject.properties.principalId - roleDefinitionIdOrName: 'Reader' - principalType: 'ServicePrincipal' - } - { - principalId: managedIdentityProject.properties.principalId - roleDefinitionIdOrName: 'Storage Account Contributor' - principalType: 'ServicePrincipal' - } - { - principalId: managedIdentityProject.properties.principalId - roleDefinitionIdOrName: 'Storage Table Data Contributor' - principalType: 'ServicePrincipal' - } - { - principalId: managedIdentityHub.properties.principalId - roleDefinitionIdOrName: 'Storage Blob Data Contributor' - principalType: 'ServicePrincipal' - } - { - principalId: managedIdentityProject.properties.principalId - roleDefinitionIdOrName: 'Storage Blob Data Contributor' - principalType: 'ServicePrincipal' - } - { - principalId: managedIdentityHub.properties.principalId - roleDefinitionIdOrName: '69566ab7-960f-475b-8e7c-b3118f30c6bd' // Storage File Data Privileged Contributor - principalType: 'ServicePrincipal' - } - { - principalId: managedIdentityProject.properties.principalId - roleDefinitionIdOrName: '69566ab7-960f-475b-8e7c-b3118f30c6bd' // Storage File Data Privileged Contributor - principalType: 'ServicePrincipal' - } - ] + roleAssignments: managedIdentityName != null + ? [ + { + principalId: userAssignedIdentity.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + { + principalId: userAssignedIdentity.properties.principalId + roleDefinitionIdOrName: 'Storage Blob Data Contributor' + principalType: 'ServicePrincipal' + } + { + principalId: userAssignedIdentity.properties.principalId + roleDefinitionIdOrName: '69566ab7-960f-475b-8e7c-b3118f30c6bd' // Storage File Data Privileged Contributor + principalType: 'ServicePrincipal' + } + ] + : null tags: tags } @@ -444,28 +407,20 @@ module containerRegistry 'br/public:avm/res/container-registry/registry:0.3.1' = networkRuleBypassOptions: 'AzureServices' zoneRedundancy: 'Enabled' trustPolicyStatus: containerRegistryConfiguration.?trustPolicyStatus ?? 'enabled' - roleAssignments: [ - { - principalId: managedIdentityHub.properties.principalId - roleDefinitionIdOrName: 'Contributor' - principalType: 'ServicePrincipal' - } - { - principalId: managedIdentityProject.properties.principalId - roleDefinitionIdOrName: 'Contributor' - principalType: 'ServicePrincipal' - } - { - principalId: managedIdentityHub.properties.principalId - roleDefinitionIdOrName: 'AcrPull' - principalType: 'ServicePrincipal' - } - { - principalId: managedIdentityProject.properties.principalId - roleDefinitionIdOrName: 'AcrPull' - principalType: 'ServicePrincipal' - } - ] + roleAssignments: managedIdentityName != null + ? [ + { + principalId: userAssignedIdentity.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + { + principalId: userAssignedIdentity.properties.principalId + roleDefinitionIdOrName: 'AcrPull' + principalType: 'ServicePrincipal' + } + ] + : null tags: tags } } @@ -478,18 +433,15 @@ module applicationInsights 'br/public:avm/res/insights/component:0.3.1' = { kind: 'web' enableTelemetry: enableTelemetry workspaceResourceId: logAnalyticsWorkspace.id - roleAssignments: [ - { - principalId: managedIdentityHub.properties.principalId - roleDefinitionIdOrName: 'Contributor' - principalType: 'ServicePrincipal' - } - { - principalId: managedIdentityProject.properties.principalId - roleDefinitionIdOrName: 'Contributor' - principalType: 'ServicePrincipal' - } - ] + roleAssignments: managedIdentityName != null + ? [ + { + principalId: userAssignedIdentity.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + ] + : null tags: tags } } @@ -509,12 +461,14 @@ module workspaceHub 'br/public:avm/res/machine-learning-services/workspace:0.5.0 workspaceHubConfig: { defaultWorkspaceResourceGroup: resourceGroup().id } - managedIdentities: { - userAssignedResourceIds: [ - managedIdentityHub.id - ] - } - primaryUserAssignedIdentity: managedIdentityHub.id + managedIdentities: managedIdentityName != null + ? { + userAssignedResourceIds: [ + userAssignedIdentity.id + ] + } + : null + primaryUserAssignedIdentity: managedIdentityName != null ? userAssignedIdentity.id : null computes: workspaceConfiguration.?computes managedNetworkSettings: { isolationMode: workspaceConfiguration.?networkIsolationMode ?? 'AllowInternetOutbound' @@ -537,19 +491,22 @@ module workspaceHub 'br/public:avm/res/machine-learning-services/workspace:0.5.0 ] : null systemDatastoresAuthMode: 'identity' - roleAssignments: [ - { - principalId: managedIdentityHub.properties.principalId - roleDefinitionIdOrName: 'Contributor' - principalType: 'ServicePrincipal' - } - ] + roleAssignments: managedIdentityName != null + ? [ + { + principalId: userAssignedIdentity.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + ] + : null tags: tags } dependsOn: workspaceHub_privateDnsZones } +// The workspace project uses a system assigned managed identity, so it can authenticate with the container registry module workspaceProject 'br/public:avm/res/machine-learning-services/workspace:0.5.0' = { name: '${uniqueString(deployment().name, location)}-project' params: { @@ -559,24 +516,15 @@ module workspaceProject 'br/public:avm/res/machine-learning-services/workspace:0 enableTelemetry: enableTelemetry kind: 'Project' hubResourceId: workspaceHub.outputs.resourceId - managedIdentities: { - userAssignedResourceIds: [ - managedIdentityProject.id - ] - } - primaryUserAssignedIdentity: managedIdentityProject.id - roleAssignments: [ - { - principalId: managedIdentityHub.properties.principalId - roleDefinitionIdOrName: 'Contributor' - principalType: 'ServicePrincipal' - } - { - principalId: managedIdentityProject.properties.principalId - roleDefinitionIdOrName: 'Contributor' - principalType: 'ServicePrincipal' - } - ] + roleAssignments: managedIdentityName != null + ? [ + { + principalId: userAssignedIdentity.properties.principalId + roleDefinitionIdOrName: 'Contributor' + principalType: 'ServicePrincipal' + } + ] + : null tags: tags } } @@ -612,30 +560,6 @@ output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id @description('The name of the log analytics workspace.') output logAnalyticsWorkspaceName string = logAnalyticsWorkspace.name -@description('The resource ID of the workspace hub user assigned managed identity.') -output managedIdentityHubResourceId string = managedIdentityHub.id - -@description('The name of the workspace hub user assigned managed identity.') -output managedIdentityHubName string = managedIdentityHub.name - -@description('The principal ID of the workspace hub user assigned managed identity.') -output managedIdentityHubPrincipalId string = managedIdentityHub.properties.principalId - -@description('The client ID of the workspace hub user assigned managed identity.') -output managedIdentityHubClientId string = managedIdentityHub.properties.clientId - -@description('The resource ID of the workspace project user assigned managed identity.') -output managedIdentityProjectResourceId string = managedIdentityProject.id - -@description('The name of the workspace project user assigned managed identity.') -output managedIdentityProjectName string = managedIdentityProject.name - -@description('The principal ID of the workspace project user assigned managed identity.') -output managedIdentityProjectPrincipalId string = managedIdentityProject.properties.principalId - -@description('The client ID of the workspace project user assigned managed identity.') -output managedIdentityProjectClientId string = managedIdentityProject.properties.clientId - @description('The resource ID of the key vault.') output keyVaultResourceId string = keyVault.outputs.resourceId @@ -663,6 +587,12 @@ output workspaceHubResourceId string = workspaceHub.outputs.resourceId @description('The name of the workspace hub.') output workspaceHubName string = workspaceHub.outputs.name +@description('The principal ID of the workspace hub system assigned identity, if applicable.') +output workspaceHubManagedIdentityPrincipalId string = workspaceHub.outputs.systemAssignedMIPrincipalId + +@description('The principal ID of the workspace project system assigned identity.') +output workspaceProjectManagedIdentityPrincipalId string = workspaceProject.outputs.systemAssignedMIPrincipalId + @description('The resource ID of the workspace project.') output workspaceProjectResourceId string = workspaceProject.outputs.resourceId @@ -697,14 +627,6 @@ output virtualMachineName string = createVirtualMachine ? virtualMachine.outputs // Definitions // // ================ // -type managedIdentityConfigurationType = { - @description('Optional. The name of the workspace hub user-assigned managed identity.') - hubName: string? - - @description('Optional. The name of the workspace project user-assigned managed identity.') - projectName: string? -}? - type logAnalyticsConfigurationType = { @description('Optional. The name of the Log Analytics workspace.') name: string? diff --git a/avm/ptn/ai-platform/baseline/main.json b/avm/ptn/ai-platform/baseline/main.json index 4fe65a2d83..a6eafbcbbd 100644 --- a/avm/ptn/ai-platform/baseline/main.json +++ b/avm/ptn/ai-platform/baseline/main.json @@ -5,34 +5,14 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "2263138345896714301" + "version": "0.30.3.12046", + "templateHash": "12442663604053803236" }, "name": "AI Platform Baseline", "description": "This module provides a secure and scalable environment for deploying AI applications on Azure.\nThe module encompasses all essential components required for building, managing, and observing AI solutions, including a machine learning workspace, observability tools, and necessary data management services.\nBy integrating with Microsoft Entra ID for secure identity management and utilizing private endpoints for services like Key Vault and Blob Storage, the module ensures secure communication and data access.", "owner": "Azure/module-maintainers" }, "definitions": { - "managedIdentityConfigurationType": { - "type": "object", - "properties": { - "hubName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the workspace hub user-assigned managed identity." - } - }, - "projectName": { - "type": "string", - "nullable": true, - "metadata": { - "description": "Optional. The name of the workspace project user-assigned managed identity." - } - } - }, - "nullable": true - }, "logAnalyticsConfigurationType": { "type": "object", "properties": { @@ -776,10 +756,11 @@ "description": "Optional. Enable/Disable usage telemetry for module." } }, - "managedIdentityConfiguration": { - "$ref": "#/definitions/managedIdentityConfigurationType", + "managedIdentityName": { + "type": "string", + "nullable": true, "metadata": { - "description": "Optional. Configuration for the user-assigned managed identities." + "description": "Optional. The name of the user assigned identity for the AI Studio hub. If not provided, the hub will use a system assigned identity." } }, "logAnalyticsConfiguration": { @@ -919,19 +900,12 @@ } } }, - "managedIdentityHub": { + "userAssignedIdentity": { + "condition": "[not(equals(parameters('managedIdentityName'), null()))]", + "existing": true, "type": "Microsoft.ManagedIdentity/userAssignedIdentities", "apiVersion": "2023-01-31", - "name": "[coalesce(tryGet(parameters('managedIdentityConfiguration'), 'hubName'), format('id-hub-{0}', parameters('name')))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]" - }, - "managedIdentityProject": { - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", - "name": "[coalesce(tryGet(parameters('managedIdentityConfiguration'), 'projectName'), format('id-project-{0}', parameters('name')))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]" + "name": "[coalesce(parameters('managedIdentityName'), 'null')]" }, "logAnalyticsWorkspace": { "type": "Microsoft.OperationalInsights/workspaces", @@ -941,16 +915,17 @@ "tags": "[parameters('tags')]" }, "resourceGroup_roleAssignment": { + "condition": "[not(equals(parameters('managedIdentityName'), null()))]", "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "name": "[guid(resourceGroup().id, parameters('name'))]", "properties": { "roleDefinitionId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "principalId": "[reference('managedIdentityHub').principalId]", + "principalId": "[reference('userAssignedIdentity').principalId]", "principalType": "ServicePrincipal" }, "dependsOn": [ - "managedIdentityHub" + "userAssignedIdentity" ] }, "storageAccount_privateDnsZones": { @@ -3860,15 +3835,7 @@ } ] }, - "roleAssignments": { - "value": [ - { - "principalId": "[reference('managedIdentityHub').principalId]", - "roleDefinitionIdOrName": "Contributor", - "principalType": "ServicePrincipal" - } - ] - } + "roleAssignments": "[if(not(equals(parameters('managedIdentityName'), null())), createObject('value', createArray(createObject('principalId', reference('userAssignedIdentity').principalId, 'roleDefinitionIdOrName', 'Contributor', 'principalType', 'ServicePrincipal'))), createObject('value', null()))]" }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -6723,7 +6690,7 @@ } }, "dependsOn": [ - "managedIdentityHub", + "userAssignedIdentity", "virtualNetwork" ] }, @@ -14047,30 +14014,7 @@ "enablePurgeProtection": { "value": "[coalesce(tryGet(parameters('keyVaultConfiguration'), 'enablePurgeProtection'), true())]" }, - "roleAssignments": { - "value": [ - { - "principalId": "[reference('managedIdentityHub').principalId]", - "roleDefinitionIdOrName": "Contributor", - "principalType": "ServicePrincipal" - }, - { - "principalId": "[reference('managedIdentityProject').principalId]", - "roleDefinitionIdOrName": "Contributor", - "principalType": "ServicePrincipal" - }, - { - "principalId": "[reference('managedIdentityHub').principalId]", - "roleDefinitionIdOrName": "Key Vault Administrator", - "principalType": "ServicePrincipal" - }, - { - "principalId": "[reference('managedIdentityProject').principalId]", - "roleDefinitionIdOrName": "Key Vault Administrator", - "principalType": "ServicePrincipal" - } - ] - }, + "roleAssignments": "[if(not(equals(parameters('managedIdentityName'), null())), createObject('value', createArray(createObject('principalId', reference('userAssignedIdentity').principalId, 'roleDefinitionIdOrName', 'Contributor', 'principalType', 'ServicePrincipal'), createObject('principalId', reference('userAssignedIdentity').principalId, 'roleDefinitionIdOrName', 'Key Vault Administrator', 'principalType', 'ServicePrincipal'))), createObject('value', null()))]", "diagnosticSettings": { "value": [ { @@ -16818,8 +16762,7 @@ }, "dependsOn": [ "logAnalyticsWorkspace", - "managedIdentityHub", - "managedIdentityProject" + "userAssignedIdentity" ] }, "storageAccount": { @@ -16863,50 +16806,7 @@ } }, "privateEndpoints": "[if(not(equals(variables('subnetResourceId'), null())), createObject('value', map(items(variables('storagePrivateDnsZones')), lambda('zone', createObject('name', format('pep-{0}-{1}', lambdaVariables('zone').value, parameters('name')), 'customNetworkInterfaceName', format('nic-{0}-{1}', lambdaVariables('zone').value, parameters('name')), 'service', lambdaVariables('zone').value, 'subnetResourceId', coalesce(variables('subnetResourceId'), ''), 'privateDnsZoneResourceIds', createArray(resourceId('Microsoft.Network/privateDnsZones', lambdaVariables('zone').key)))))), createObject('value', null()))]", - "roleAssignments": { - "value": [ - { - "principalId": "[reference('managedIdentityHub').principalId]", - "roleDefinitionIdOrName": "Contributor", - "principalType": "ServicePrincipal" - }, - { - "principalId": "[reference('managedIdentityProject').principalId]", - "roleDefinitionIdOrName": "Reader", - "principalType": "ServicePrincipal" - }, - { - "principalId": "[reference('managedIdentityProject').principalId]", - "roleDefinitionIdOrName": "Storage Account Contributor", - "principalType": "ServicePrincipal" - }, - { - "principalId": "[reference('managedIdentityProject').principalId]", - "roleDefinitionIdOrName": "Storage Table Data Contributor", - "principalType": "ServicePrincipal" - }, - { - "principalId": "[reference('managedIdentityHub').principalId]", - "roleDefinitionIdOrName": "Storage Blob Data Contributor", - "principalType": "ServicePrincipal" - }, - { - "principalId": "[reference('managedIdentityProject').principalId]", - "roleDefinitionIdOrName": "Storage Blob Data Contributor", - "principalType": "ServicePrincipal" - }, - { - "principalId": "[reference('managedIdentityHub').principalId]", - "roleDefinitionIdOrName": "69566ab7-960f-475b-8e7c-b3118f30c6bd", - "principalType": "ServicePrincipal" - }, - { - "principalId": "[reference('managedIdentityProject').principalId]", - "roleDefinitionIdOrName": "69566ab7-960f-475b-8e7c-b3118f30c6bd", - "principalType": "ServicePrincipal" - } - ] - }, + "roleAssignments": "[if(not(equals(parameters('managedIdentityName'), null())), createObject('value', createArray(createObject('principalId', reference('userAssignedIdentity').principalId, 'roleDefinitionIdOrName', 'Contributor', 'principalType', 'ServicePrincipal'), createObject('principalId', reference('userAssignedIdentity').principalId, 'roleDefinitionIdOrName', 'Storage Blob Data Contributor', 'principalType', 'ServicePrincipal'), createObject('principalId', reference('userAssignedIdentity').principalId, 'roleDefinitionIdOrName', '69566ab7-960f-475b-8e7c-b3118f30c6bd', 'principalType', 'ServicePrincipal'))), createObject('value', null()))]", "tags": { "value": "[parameters('tags')]" } @@ -21570,9 +21470,8 @@ }, "dependsOn": [ "virtualNetwork::defaultSubnet", - "managedIdentityHub", - "managedIdentityProject", - "storageAccount_privateDnsZones" + "storageAccount_privateDnsZones", + "userAssignedIdentity" ] }, "containerRegistry": { @@ -21609,30 +21508,7 @@ "trustPolicyStatus": { "value": "[coalesce(tryGet(parameters('containerRegistryConfiguration'), 'trustPolicyStatus'), 'enabled')]" }, - "roleAssignments": { - "value": [ - { - "principalId": "[reference('managedIdentityHub').principalId]", - "roleDefinitionIdOrName": "Contributor", - "principalType": "ServicePrincipal" - }, - { - "principalId": "[reference('managedIdentityProject').principalId]", - "roleDefinitionIdOrName": "Contributor", - "principalType": "ServicePrincipal" - }, - { - "principalId": "[reference('managedIdentityHub').principalId]", - "roleDefinitionIdOrName": "AcrPull", - "principalType": "ServicePrincipal" - }, - { - "principalId": "[reference('managedIdentityProject').principalId]", - "roleDefinitionIdOrName": "AcrPull", - "principalType": "ServicePrincipal" - } - ] - }, + "roleAssignments": "[if(not(equals(parameters('managedIdentityName'), null())), createObject('value', createArray(createObject('principalId', reference('userAssignedIdentity').principalId, 'roleDefinitionIdOrName', 'Contributor', 'principalType', 'ServicePrincipal'), createObject('principalId', reference('userAssignedIdentity').principalId, 'roleDefinitionIdOrName', 'AcrPull', 'principalType', 'ServicePrincipal'))), createObject('value', null()))]", "tags": { "value": "[parameters('tags')]" } @@ -23754,8 +23630,7 @@ } }, "dependsOn": [ - "managedIdentityHub", - "managedIdentityProject" + "userAssignedIdentity" ] }, "applicationInsights": { @@ -23783,20 +23658,7 @@ "workspaceResourceId": { "value": "[resourceId('Microsoft.OperationalInsights/workspaces', coalesce(tryGet(parameters('logAnalyticsConfiguration'), 'name'), format('log-{0}', parameters('name'))))]" }, - "roleAssignments": { - "value": [ - { - "principalId": "[reference('managedIdentityHub').principalId]", - "roleDefinitionIdOrName": "Contributor", - "principalType": "ServicePrincipal" - }, - { - "principalId": "[reference('managedIdentityProject').principalId]", - "roleDefinitionIdOrName": "Contributor", - "principalType": "ServicePrincipal" - } - ] - }, + "roleAssignments": "[if(not(equals(parameters('managedIdentityName'), null())), createObject('value', createArray(createObject('principalId', reference('userAssignedIdentity').principalId, 'roleDefinitionIdOrName', 'Contributor', 'principalType', 'ServicePrincipal'))), createObject('value', null()))]", "tags": { "value": "[parameters('tags')]" } @@ -24397,8 +24259,7 @@ }, "dependsOn": [ "logAnalyticsWorkspace", - "managedIdentityHub", - "managedIdentityProject" + "userAssignedIdentity" ] }, "workspaceHub": { @@ -24443,16 +24304,8 @@ "defaultWorkspaceResourceGroup": "[resourceGroup().id]" } }, - "managedIdentities": { - "value": { - "userAssignedResourceIds": [ - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', coalesce(tryGet(parameters('managedIdentityConfiguration'), 'hubName'), format('id-hub-{0}', parameters('name'))))]" - ] - } - }, - "primaryUserAssignedIdentity": { - "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', coalesce(tryGet(parameters('managedIdentityConfiguration'), 'hubName'), format('id-hub-{0}', parameters('name'))))]" - }, + "managedIdentities": "[if(not(equals(parameters('managedIdentityName'), null())), createObject('value', createObject('userAssignedResourceIds', createArray(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', coalesce(parameters('managedIdentityName'), 'null'))))), createObject('value', null()))]", + "primaryUserAssignedIdentity": "[if(not(equals(parameters('managedIdentityName'), null())), createObject('value', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', coalesce(parameters('managedIdentityName'), 'null'))), createObject('value', null()))]", "computes": { "value": "[tryGet(parameters('workspaceConfiguration'), 'computes')]" }, @@ -24466,15 +24319,7 @@ "systemDatastoresAuthMode": { "value": "identity" }, - "roleAssignments": { - "value": [ - { - "principalId": "[reference('managedIdentityHub').principalId]", - "roleDefinitionIdOrName": "Contributor", - "principalType": "ServicePrincipal" - } - ] - }, + "roleAssignments": "[if(not(equals(parameters('managedIdentityName'), null())), createObject('value', createArray(createObject('principalId', reference('userAssignedIdentity').principalId, 'roleDefinitionIdOrName', 'Contributor', 'principalType', 'ServicePrincipal'))), createObject('value', null()))]", "tags": { "value": "[parameters('tags')]" } @@ -26720,8 +26565,8 @@ "containerRegistry", "virtualNetwork::defaultSubnet", "keyVault", - "managedIdentityHub", "storageAccount", + "userAssignedIdentity", "workspaceHub_privateDnsZones" ] }, @@ -26753,30 +26598,7 @@ "hubResourceId": { "value": "[reference('workspaceHub').outputs.resourceId.value]" }, - "managedIdentities": { - "value": { - "userAssignedResourceIds": [ - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', coalesce(tryGet(parameters('managedIdentityConfiguration'), 'projectName'), format('id-project-{0}', parameters('name'))))]" - ] - } - }, - "primaryUserAssignedIdentity": { - "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', coalesce(tryGet(parameters('managedIdentityConfiguration'), 'projectName'), format('id-project-{0}', parameters('name'))))]" - }, - "roleAssignments": { - "value": [ - { - "principalId": "[reference('managedIdentityHub').principalId]", - "roleDefinitionIdOrName": "Contributor", - "principalType": "ServicePrincipal" - }, - { - "principalId": "[reference('managedIdentityProject').principalId]", - "roleDefinitionIdOrName": "Contributor", - "principalType": "ServicePrincipal" - } - ] - }, + "roleAssignments": "[if(not(equals(parameters('managedIdentityName'), null())), createObject('value', createArray(createObject('principalId', reference('userAssignedIdentity').principalId, 'roleDefinitionIdOrName', 'Contributor', 'principalType', 'ServicePrincipal'))), createObject('value', null()))]", "tags": { "value": "[parameters('tags')]" } @@ -29018,8 +28840,7 @@ } }, "dependsOn": [ - "managedIdentityHub", - "managedIdentityProject", + "userAssignedIdentity", "workspaceHub" ] } @@ -29088,62 +28909,6 @@ }, "value": "[coalesce(tryGet(parameters('logAnalyticsConfiguration'), 'name'), format('log-{0}', parameters('name')))]" }, - "managedIdentityHubResourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the workspace hub user assigned managed identity." - }, - "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', coalesce(tryGet(parameters('managedIdentityConfiguration'), 'hubName'), format('id-hub-{0}', parameters('name'))))]" - }, - "managedIdentityHubName": { - "type": "string", - "metadata": { - "description": "The name of the workspace hub user assigned managed identity." - }, - "value": "[coalesce(tryGet(parameters('managedIdentityConfiguration'), 'hubName'), format('id-hub-{0}', parameters('name')))]" - }, - "managedIdentityHubPrincipalId": { - "type": "string", - "metadata": { - "description": "The principal ID of the workspace hub user assigned managed identity." - }, - "value": "[reference('managedIdentityHub').principalId]" - }, - "managedIdentityHubClientId": { - "type": "string", - "metadata": { - "description": "The client ID of the workspace hub user assigned managed identity." - }, - "value": "[reference('managedIdentityHub').clientId]" - }, - "managedIdentityProjectResourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the workspace project user assigned managed identity." - }, - "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', coalesce(tryGet(parameters('managedIdentityConfiguration'), 'projectName'), format('id-project-{0}', parameters('name'))))]" - }, - "managedIdentityProjectName": { - "type": "string", - "metadata": { - "description": "The name of the workspace project user assigned managed identity." - }, - "value": "[coalesce(tryGet(parameters('managedIdentityConfiguration'), 'projectName'), format('id-project-{0}', parameters('name')))]" - }, - "managedIdentityProjectPrincipalId": { - "type": "string", - "metadata": { - "description": "The principal ID of the workspace project user assigned managed identity." - }, - "value": "[reference('managedIdentityProject').principalId]" - }, - "managedIdentityProjectClientId": { - "type": "string", - "metadata": { - "description": "The client ID of the workspace project user assigned managed identity." - }, - "value": "[reference('managedIdentityProject').clientId]" - }, "keyVaultResourceId": { "type": "string", "metadata": { @@ -29207,6 +28972,20 @@ }, "value": "[reference('workspaceHub').outputs.name.value]" }, + "workspaceHubManagedIdentityPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the workspace hub system assigned identity, if applicable." + }, + "value": "[reference('workspaceHub').outputs.systemAssignedMIPrincipalId.value]" + }, + "workspaceProjectManagedIdentityPrincipalId": { + "type": "string", + "metadata": { + "description": "The principal ID of the workspace project system assigned identity." + }, + "value": "[reference('workspaceProject').outputs.systemAssignedMIPrincipalId.value]" + }, "workspaceProjectResourceId": { "type": "string", "metadata": { diff --git a/avm/ptn/ai-platform/baseline/tests/e2e/max/dependencies.bicep b/avm/ptn/ai-platform/baseline/tests/e2e/max/dependencies.bicep index d2ea57bb6a..1ce964acc0 100644 --- a/avm/ptn/ai-platform/baseline/tests/e2e/max/dependencies.bicep +++ b/avm/ptn/ai-platform/baseline/tests/e2e/max/dependencies.bicep @@ -4,6 +4,9 @@ param location string = resourceGroup().location @description('Required. The name of the Storage Account to create.') param storageAccountName string +@description('Required. The name of the Managed Identity to create.') +param managedIdentityName string + @description('Required. The name of the Maintenance Configuration to create.') param maintenanceConfigurationName string @@ -13,6 +16,11 @@ param networkSecurityGroupName string @description('Required. The name of the Bastion Network Security Group to create.') param networkSecurityGroupBastionName 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 @@ -196,6 +204,9 @@ resource networkSecurityGroupBastion 'Microsoft.Network/networkSecurityGroups@20 @description('The resource ID of the created Storage Account.') output storageAccountResourceId string = storageAccount.id +@description('The name of the created Managed Identity.') +output managedIdentityName string = managedIdentity.name + @description('The resource ID of the created Network Security Group.') output networkSecurityGroupResourceId string = networkSecurityGroup.id diff --git a/avm/ptn/ai-platform/baseline/tests/e2e/max/main.test.bicep b/avm/ptn/ai-platform/baseline/tests/e2e/max/main.test.bicep index 240496050a..a8beac49bf 100644 --- a/avm/ptn/ai-platform/baseline/tests/e2e/max/main.test.bicep +++ b/avm/ptn/ai-platform/baseline/tests/e2e/max/main.test.bicep @@ -44,6 +44,7 @@ module nestedDependencies 'dependencies.bicep' = { params: { location: resourceLocation storageAccountName: 'dep${namePrefix}st${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-mi-${serviceShort}' maintenanceConfigurationName: 'dep-${namePrefix}-mc-${serviceShort}' networkSecurityGroupName: 'dep${namePrefix}nsg${serviceShort}' networkSecurityGroupBastionName: 'dep-${namePrefix}-nsg-bastion-${serviceShort}' @@ -61,10 +62,7 @@ module testDeployment '../../../main.bicep' = [ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}' - managedIdentityConfiguration: { - hubName: '${namePrefix}-id-hub-${serviceShort}' - projectName: '${namePrefix}-id-project-${serviceShort}' - } + managedIdentityName: nestedDependencies.outputs.managedIdentityName logAnalyticsConfiguration: { name: '${namePrefix}-log-${serviceShort}' } diff --git a/avm/ptn/ai-platform/baseline/tests/e2e/waf-aligned/dependencies.bicep b/avm/ptn/ai-platform/baseline/tests/e2e/waf-aligned/dependencies.bicep index 299d71ad37..fbb0fe92d4 100644 --- a/avm/ptn/ai-platform/baseline/tests/e2e/waf-aligned/dependencies.bicep +++ b/avm/ptn/ai-platform/baseline/tests/e2e/waf-aligned/dependencies.bicep @@ -1,12 +1,20 @@ @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 Maintenance Configuration to create.') param maintenanceConfigurationName 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 @@ -16,6 +24,18 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { kind: 'StorageV2' } +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(resourceGroup().id, storageAccount.id, managedIdentity.id) + properties: { + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'b556d68e-0be0-4f35-a333-ad7ee1ce17ea' // Azure AI Enterprise Network Connection Approver + ) + principalId: managedIdentity.properties.principalId + principalType: 'ServicePrincipal' + } +} + resource maintenanceConfiguration 'Microsoft.Maintenance/maintenanceConfigurations@2023-10-01-preview' = { name: maintenanceConfigurationName location: location @@ -46,5 +66,8 @@ resource maintenanceConfiguration 'Microsoft.Maintenance/maintenanceConfiguratio @description('The resource ID of the created Storage Account.') output storageAccountResourceId string = storageAccount.id +@description('The name of the created Managed Identity.') +output managedIdentityName string = managedIdentity.name + @description('The resource ID of the maintenance configuration.') output maintenanceConfigurationResourceId string = maintenanceConfiguration.id diff --git a/avm/ptn/ai-platform/baseline/tests/e2e/waf-aligned/main.test.bicep b/avm/ptn/ai-platform/baseline/tests/e2e/waf-aligned/main.test.bicep index 21d1ba8934..ec39ff7d58 100644 --- a/avm/ptn/ai-platform/baseline/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/ptn/ai-platform/baseline/tests/e2e/waf-aligned/main.test.bicep @@ -44,6 +44,7 @@ module nestedDependencies 'dependencies.bicep' = { name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' params: { storageAccountName: 'dep${namePrefix}st${serviceShort}' + managedIdentityName: 'dep-${namePrefix}-mi-${serviceShort}' maintenanceConfigurationName: 'dep-${namePrefix}-mc-${serviceShort}' location: enforcedLocation } @@ -60,6 +61,7 @@ module testDeployment '../../../main.bicep' = [ name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}${substring(uniqueString(baseTime), 0, 3)}' + managedIdentityName: nestedDependencies.outputs.managedIdentityName virtualMachineConfiguration: { adminUsername: 'localAdminUser' adminPassword: password diff --git a/avm/ptn/ai-platform/baseline/version.json b/avm/ptn/ai-platform/baseline/version.json index aa34c4d0f5..41fc8c654f 100644 --- a/avm/ptn/ai-platform/baseline/version.json +++ b/avm/ptn/ai-platform/baseline/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.4", + "version": "0.5", "pathFilters": [ "./main.json" ] From 432219fbad171fbb51886495b260820412ad0664 Mon Sep 17 00:00:00 2001 From: Ahmad Abdalla <28486158+ahmadabdalla@users.noreply.github.com> Date: Tue, 24 Sep 2024 18:47:42 +1000 Subject: [PATCH 32/57] fix: Add explicit dependsOn in the privateLinkScope_privateEndpoints module for `avm/res/insights/private-link-scope` (#3348) ## Description Fixes #3208 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.insights.private-link-scope](https://github.com/ahmadabdalla/bicep-registry-modules/actions/workflows/avm.res.insights.private-link-scope.yml/badge.svg?branch=users%2Fahmad%2F3208_pls)](https://github.com/ahmadabdalla/bicep-registry-modules/actions/workflows/avm.res.insights.private-link-scope.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/insights/private-link-scope/README.md | 4 ++-- avm/res/insights/private-link-scope/main.bicep | 3 +++ avm/res/insights/private-link-scope/main.json | 13 +++++++------ .../private-link-scope/scoped-resource/main.json | 4 ++-- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/avm/res/insights/private-link-scope/README.md b/avm/res/insights/private-link-scope/README.md index 4dda77b209..e8ef974f3c 100644 --- a/avm/res/insights/private-link-scope/README.md +++ b/avm/res/insights/private-link-scope/README.md @@ -770,7 +770,7 @@ module privateLinkScope 'br/public:avm/res/insights/private-link-scope: | Parameter | Type | Description | | :-- | :-- | :-- | -| [`accessModeSettings`](#parameter-accessmodesettings) | object | Specifies the access mode of ingestion or queries through associated private endpoints in scope. For security reasons, it is recommended to use PrivateOnly whenever possible to avoid data exfiltration.

* Private Only - This mode allows the connected virtual network to reach only Private Link resources. It is the most secure mode and is set as the default when the `privateEndpoints` parameter is configured.

* Open - Allows the connected virtual network to reach both Private Link resources and the resources not in the AMPLS resource. Data exfiltration cannot be prevented in this mode. | +| [`accessModeSettings`](#parameter-accessmodesettings) | object | Specifies the access mode of ingestion or queries through associated private endpoints in scope. For security reasons, it is recommended to use PrivateOnly whenever possible to avoid data exfiltration.

* Private Only - This mode allows the connected virtual network to reach only Private Link resources. It is the most secure mode and is set as the default when the `privateEndpoints` parameter is configured.

* Open - Allows the connected virtual network to reach both Private Link resources and the resources not in the AMPLS resource. Data exfiltration cannot be prevented in this mode. | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | | [`location`](#parameter-location) | string | The location of the private link scope. Should be global. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | @@ -788,7 +788,7 @@ Name of the private link scope. ### Parameter: `accessModeSettings` -Specifies the access mode of ingestion or queries through associated private endpoints in scope. For security reasons, it is recommended to use PrivateOnly whenever possible to avoid data exfiltration.

* Private Only - This mode allows the connected virtual network to reach only Private Link resources. It is the most secure mode and is set as the default when the `privateEndpoints` parameter is configured.

* Open - Allows the connected virtual network to reach both Private Link resources and the resources not in the AMPLS resource. Data exfiltration cannot be prevented in this mode. +Specifies the access mode of ingestion or queries through associated private endpoints in scope. For security reasons, it is recommended to use PrivateOnly whenever possible to avoid data exfiltration.

* Private Only - This mode allows the connected virtual network to reach only Private Link resources. It is the most secure mode and is set as the default when the `privateEndpoints` parameter is configured.

* Open - Allows the connected virtual network to reach both Private Link resources and the resources not in the AMPLS resource. Data exfiltration cannot be prevented in this mode. - Required: No - Type: object diff --git a/avm/res/insights/private-link-scope/main.bicep b/avm/res/insights/private-link-scope/main.bicep index 50534b0383..a6666eec33 100644 --- a/avm/res/insights/private-link-scope/main.bicep +++ b/avm/res/insights/private-link-scope/main.bicep @@ -203,6 +203,9 @@ module privateLinkScope_privateEndpoints 'br/public:avm/res/network/private-endp applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds customNetworkInterfaceName: privateEndpoint.?customNetworkInterfaceName } + dependsOn: [ + privateLinkScope_scopedResource + ] } ] diff --git a/avm/res/insights/private-link-scope/main.json b/avm/res/insights/private-link-scope/main.json index c1efbd235d..ecb92303a1 100644 --- a/avm/res/insights/private-link-scope/main.json +++ b/avm/res/insights/private-link-scope/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "9210191494689145931" + "version": "0.30.3.12046", + "templateHash": "5947451992668490696" }, "name": "Azure Monitor Private Link Scopes", "description": "This module deploys an Azure Monitor Private Link Scope.", @@ -430,7 +430,7 @@ "accessModeSettings": { "$ref": "#/definitions/accessModeType", "metadata": { - "description": "Optional. Specifies the access mode of ingestion or queries through associated private endpoints in scope. For security reasons, it is recommended to use PrivateOnly whenever possible to avoid data exfiltration.\n\n * Private Only - This mode allows the connected virtual network to reach only Private Link resources. It is the most secure mode and is set as the default when the `privateEndpoints` parameter is configured.\n * Open - Allows the connected virtual network to reach both Private Link resources and the resources not in the AMPLS resource. Data exfiltration cannot be prevented in this mode." + "description": "Optional. Specifies the access mode of ingestion or queries through associated private endpoints in scope. For security reasons, it is recommended to use PrivateOnly whenever possible to avoid data exfiltration.\n\n* Private Only - This mode allows the connected virtual network to reach only Private Link resources. It is the most secure mode and is set as the default when the `privateEndpoints` parameter is configured.\n* Open - Allows the connected virtual network to reach both Private Link resources and the resources not in the AMPLS resource. Data exfiltration cannot be prevented in this mode." } }, "location": { @@ -601,8 +601,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15704480901641345186" + "version": "0.30.3.12046", + "templateHash": "16277811786602972091" }, "name": "Private Link Scope Scoped Resources", "description": "This module deploys a Private Link Scope Scoped Resource.", @@ -1432,7 +1432,8 @@ } }, "dependsOn": [ - "privateLinkScope" + "privateLinkScope", + "privateLinkScope_scopedResource" ] } }, diff --git a/avm/res/insights/private-link-scope/scoped-resource/main.json b/avm/res/insights/private-link-scope/scoped-resource/main.json index 57e0b76f73..430361afb5 100644 --- a/avm/res/insights/private-link-scope/scoped-resource/main.json +++ b/avm/res/insights/private-link-scope/scoped-resource/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15704480901641345186" + "version": "0.30.3.12046", + "templateHash": "16277811786602972091" }, "name": "Private Link Scope Scoped Resources", "description": "This module deploys a Private Link Scope Scoped Resource.", From 33030a858a3cdbc466a1f4b4309b3725ea9743a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Tue, 24 Sep 2024 10:49:54 +0200 Subject: [PATCH 33/57] fix: `ptn/deployment-script/import-image-to-acr` overwriteExistingImage (#3350) ## Description Setting overwriteExistingImage to false, breaks the module as it will end with an error. Fixes #3349 ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.deployment-script.import-image-to-acr](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.ptn.deployment-script.import-image-to-acr.yml/badge.svg?branch=import-image-to-acr-fix)](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.ptn.deployment-script.import-image-to-acr.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [x] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/ptn/deployment-script/import-image-to-acr/main.bicep | 2 -- avm/ptn/deployment-script/import-image-to-acr/main.json | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/avm/ptn/deployment-script/import-image-to-acr/main.bicep b/avm/ptn/deployment-script/import-image-to-acr/main.bicep index 61534011ce..e6c532e567 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/main.bicep +++ b/avm/ptn/deployment-script/import-image-to-acr/main.bicep @@ -189,8 +189,6 @@ module imageImport 'br/public:avm/res/resources/deployment-script:0.4.0' = { containerGroupName: '${resourceGroup().name}-infrastructure' subnetResourceIds: subnetResourceIds scriptContent: '''#!/bin/bash - set -e - echo "Waiting on RBAC replication ($initialDelay)\n" sleep $initialDelay diff --git a/avm/ptn/deployment-script/import-image-to-acr/main.json b/avm/ptn/deployment-script/import-image-to-acr/main.json index 4f8a4504ae..6bfe24d5cd 100644 --- a/avm/ptn/deployment-script/import-image-to-acr/main.json +++ b/avm/ptn/deployment-script/import-image-to-acr/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "13166673091432959100" + "version": "0.30.3.12046", + "templateHash": "17375159703541878382" }, "name": "import-image-to-acr", "description": "This modules deployes an image to an Azure Container Registry.", @@ -368,7 +368,7 @@ "value": "[parameters('subnetResourceIds')]" }, "scriptContent": { - "value": "#!/bin/bash\n set -e\n\n echo \"Waiting on RBAC replication ($initialDelay)\\n\"\n sleep $initialDelay\n\n # retry loop to catch errors (usually RBAC delays, but 'Error copying blobs' is also not unheard of)\n retryLoopCount=0\n until [ $retryLoopCount -ge $retryMax ]\n do\n echo \"Importing Image ($retryLoopCount): $imageName into ACR: $acrName\\n\"\n if [ $overwriteExistingImage = 'true' ]; then\n if [ -n \"$sourceRegistryUsername\" ] && [ -n \"$sourceRegistryPassword\" ]; then\n az acr import -n $acrName --source $imageName --image $newImageName --force --username $sourceRegistryUsername --password $sourceRegistryPassword\n else\n az acr import -n $acrName --source $imageName --image $newImageName --force\n fi\n else\n if [ -n \"$sourceRegistryUsername\" ] && [ -n \"$sourceRegistryPassword\" ]; then\n az acr import -n $acrName --source $imageName --image $newImageName --username $sourceRegistryUsername --password $sourceRegistryPassword\n else\n az acr import -n $acrName --source $imageName --image $newImageName\n fi\n fi\n\n sleep $retrySleep\n retryLoopCount=$((retryLoopCount+1))\n done\n\n echo \"done\\n\"" + "value": "#!/bin/bash\n echo \"Waiting on RBAC replication ($initialDelay)\\n\"\n sleep $initialDelay\n\n # retry loop to catch errors (usually RBAC delays, but 'Error copying blobs' is also not unheard of)\n retryLoopCount=0\n until [ $retryLoopCount -ge $retryMax ]\n do\n echo \"Importing Image ($retryLoopCount): $imageName into ACR: $acrName\\n\"\n if [ $overwriteExistingImage = 'true' ]; then\n if [ -n \"$sourceRegistryUsername\" ] && [ -n \"$sourceRegistryPassword\" ]; then\n az acr import -n $acrName --source $imageName --image $newImageName --force --username $sourceRegistryUsername --password $sourceRegistryPassword\n else\n az acr import -n $acrName --source $imageName --image $newImageName --force\n fi\n else\n if [ -n \"$sourceRegistryUsername\" ] && [ -n \"$sourceRegistryPassword\" ]; then\n az acr import -n $acrName --source $imageName --image $newImageName --username $sourceRegistryUsername --password $sourceRegistryPassword\n else\n az acr import -n $acrName --source $imageName --image $newImageName\n fi\n fi\n\n sleep $retrySleep\n retryLoopCount=$((retryLoopCount+1))\n done\n\n echo \"done\\n\"" } }, "template": { From f08da65f2c6d3e53e8339b74bb33bfe8e20c361a Mon Sep 17 00:00:00 2001 From: Chinedum Echeta <60179183+cecheta@users.noreply.github.com> Date: Wed, 25 Sep 2024 01:56:19 -0700 Subject: [PATCH 34/57] feat: `avm/ptn/ai-platform/baseline` Use VNet AVM (#3353) ## Description This PR is essentially a revert of https://github.com/Azure/bicep-registry-modules/pull/3054, plus updating the version of the VNet module to the latest version. The VNet module now creates subnets as child modules, therefore we can use it instead of creating the resources ourselves. ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.ai-platform.baseline](https://github.com/cecheta/bicep-registry-modules/actions/workflows/avm.ptn.ai-platform.baseline.yml/badge.svg?branch=vnet-module)](https://github.com/cecheta/bicep-registry-modules/actions/workflows/avm.ptn.ai-platform.baseline.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [x] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/ptn/ai-platform/baseline/README.md | 6 +- avm/ptn/ai-platform/baseline/main.bicep | 88 +- avm/ptn/ai-platform/baseline/main.json | 1706 +++++++++++++++++++-- avm/ptn/ai-platform/baseline/version.json | 2 +- 4 files changed, 1644 insertions(+), 158 deletions(-) diff --git a/avm/ptn/ai-platform/baseline/README.md b/avm/ptn/ai-platform/baseline/README.md index dfaf48f4a5..39a480afb9 100644 --- a/avm/ptn/ai-platform/baseline/README.md +++ b/avm/ptn/ai-platform/baseline/README.md @@ -51,13 +51,14 @@ By integrating with Microsoft Entra ID for secure identity management and utiliz | `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) | -| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | | `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | -| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | | `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Network/publicIPAddresses` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-09-01/publicIPAddresses) | | `Microsoft.Network/virtualNetworks` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks) | | `Microsoft.Network/virtualNetworks/subnets` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks/subnets) | +| `Microsoft.Network/virtualNetworks/virtualNetworkPeerings` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks/virtualNetworkPeerings) | | `Microsoft.OperationalInsights/workspaces` | [2023-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.OperationalInsights/2023-09-01/workspaces) | | `Microsoft.RecoveryServices/vaults/backupFabrics/protectionContainers/protectedItems` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.RecoveryServices/2023-01-01/vaults/backupFabrics/protectionContainers/protectedItems) | | `Microsoft.Storage/storageAccounts` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts) | @@ -1150,6 +1151,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | `br/public:avm/res/network/bastion-host:0.2.2` | Remote reference | | `br/public:avm/res/network/network-security-group:0.3.1` | Remote reference | | `br/public:avm/res/network/private-dns-zone:0.3.1` | Remote reference | +| `br/public:avm/res/network/virtual-network:0.4.0` | Remote reference | | `br/public:avm/res/storage/storage-account:0.11.0` | Remote reference | ## Data Collection diff --git a/avm/ptn/ai-platform/baseline/main.bicep b/avm/ptn/ai-platform/baseline/main.bicep index 29b505f083..cfa8b0f7d7 100644 --- a/avm/ptn/ai-platform/baseline/main.bicep +++ b/avm/ptn/ai-platform/baseline/main.bicep @@ -59,7 +59,7 @@ var createVirtualMachine = createVirtualNetwork && virtualMachineConfiguration.? var createDefaultNsg = virtualNetworkConfiguration.?subnet.networkSecurityGroupResourceId == null -var subnetResourceId = createVirtualNetwork ? virtualNetwork::defaultSubnet.id : null +var subnetResourceId = createVirtualNetwork ? virtualNetwork.outputs.subnetResourceIds[0] : null var mlTargetSubResource = 'amlworkspace' @@ -103,7 +103,7 @@ module storageAccount_privateDnsZones 'br/public:avm/res/network/private-dns-zon name: zone virtualNetworkLinks: [ { - virtualNetworkResourceId: virtualNetwork.id + virtualNetworkResourceId: virtualNetwork.outputs.resourceId } ] } @@ -117,7 +117,7 @@ module workspaceHub_privateDnsZones 'br/public:avm/res/network/private-dns-zone: name: zone virtualNetworkLinks: [ { - virtualNetworkResourceId: virtualNetwork.id + virtualNetworkResourceId: virtualNetwork.outputs.resourceId } ] roleAssignments: managedIdentityName != null @@ -160,47 +160,37 @@ module defaultNetworkSecurityGroup 'br/public:avm/res/network/network-security-g } } -// Not using the br/public:avm/res/network/virtual-network module here to -// allow consumers of the module to add subnets from outside of the module -// https://github.com/Azure/bicep-registry-modules/issues/2689 -resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-01-01' = if (createVirtualNetwork) { - name: virtualNetworkConfiguration.?name ?? 'vnet-${name}' - location: location - tags: tags - properties: { - addressSpace: { - addressPrefixes: [ - virtualNetworkConfiguration.?addressPrefix ?? '10.0.0.0/16' - ] - } - } - - resource defaultSubnet 'subnets@2024-01-01' = { - name: virtualNetworkConfiguration.?subnet.name ?? 'default' - properties: { - addressPrefix: virtualNetworkConfiguration.?subnet.addressPrefix ?? '10.0.0.0/24' - networkSecurityGroup: { - id: createDefaultNsg - ? defaultNetworkSecurityGroup.outputs.resourceId - : virtualNetworkConfiguration.?subnet.networkSecurityGroupResourceId - } - } - } - - resource bastionSubnet 'subnets@2024-01-01' = if (createBastion) { - name: 'AzureBastionSubnet' - properties: { - addressPrefix: bastionConfiguration.?subnetAddressPrefix ?? '10.0.1.0/26' - networkSecurityGroup: bastionConfiguration.?networkSecurityGroupResourceId != null - ? { - id: bastionConfiguration.?networkSecurityGroupResourceId - } - : null - } - - dependsOn: [ - defaultSubnet +module virtualNetwork 'br/public:avm/res/network/virtual-network:0.4.0' = if (createVirtualNetwork) { + name: '${uniqueString(deployment().name, location)}-virtual-network' + params: { + name: virtualNetworkConfiguration.?name ?? 'vnet-${name}' + location: location + enableTelemetry: enableTelemetry + addressPrefixes: [ + virtualNetworkConfiguration.?addressPrefix ?? '10.0.0.0/16' ] + subnets: union( + // The default subnet **must** be the first in the subnets array + [ + { + addressPrefix: virtualNetworkConfiguration.?subnet.addressPrefix ?? '10.0.0.0/24' + name: virtualNetworkConfiguration.?subnet.name ?? 'default' + networkSecurityGroupResourceId: createDefaultNsg + ? defaultNetworkSecurityGroup.outputs.resourceId + : virtualNetworkConfiguration.?subnet.networkSecurityGroupResourceId + } + ], + createBastion + ? [ + { + addressPrefix: bastionConfiguration.?subnetAddressPrefix ?? '10.0.1.0/26' + name: 'AzureBastionSubnet' + networkSecurityGroupResourceId: bastionConfiguration.?networkSecurityGroupResourceId + } + ] + : [] + ) + tags: tags } } @@ -211,7 +201,7 @@ module bastion 'br/public:avm/res/network/bastion-host:0.2.2' = if (createBastio location: location skuName: bastionConfiguration.?sku ?? 'Standard' enableTelemetry: enableTelemetry - virtualNetworkResourceId: virtualNetwork.id + virtualNetworkResourceId: virtualNetwork.outputs.resourceId disableCopyPaste: bastionConfiguration.?disableCopyPaste enableFileCopy: bastionConfiguration.?enableFileCopy enableIpConnect: bastionConfiguration.?enableIpConnect @@ -240,7 +230,7 @@ module virtualMachine 'br/public:avm/res/compute/virtual-machine:0.5.3' = if (cr { name: virtualMachineConfiguration.?nicConfigurationConfiguration.ipConfigName ?? 'nic-vm-${name}-ipconfig' privateIPAllocationMethod: virtualMachineConfiguration.?nicConfigurationConfiguration.privateIPAllocationMethod ?? 'Dynamic' - subnetResourceId: virtualNetwork::defaultSubnet.id + subnetResourceId: virtualNetwork.outputs.subnetResourceIds[0] } ] } @@ -600,16 +590,16 @@ output workspaceProjectResourceId string = workspaceProject.outputs.resourceId output workspaceProjectName string = workspaceProject.outputs.name @description('The resource ID of the virtual network.') -output virtualNetworkResourceId string = createVirtualNetwork ? virtualNetwork.id : '' +output virtualNetworkResourceId string = createVirtualNetwork ? virtualNetwork.outputs.resourceId : '' @description('The name of the virtual network.') -output virtualNetworkName string = createVirtualNetwork ? virtualNetwork.name : '' +output virtualNetworkName string = createVirtualNetwork ? virtualNetwork.outputs.name : '' @description('The resource ID of the subnet in the virtual network.') -output virtualNetworkSubnetResourceId string = createVirtualNetwork ? virtualNetwork::defaultSubnet.id : '' +output virtualNetworkSubnetResourceId string = createVirtualNetwork ? virtualNetwork.outputs.subnetResourceIds[0] : '' @description('The name of the subnet in the virtual network.') -output virtualNetworkSubnetName string = createVirtualNetwork ? virtualNetwork::defaultSubnet.name : '' +output virtualNetworkSubnetName string = createVirtualNetwork ? virtualNetwork.outputs.subnetNames[0] : '' @description('The resource ID of the Azure Bastion host.') output bastionResourceId string = createBastion ? bastion.outputs.resourceId : '' diff --git a/avm/ptn/ai-platform/baseline/main.json b/avm/ptn/ai-platform/baseline/main.json index a6eafbcbbd..9da789cd5f 100644 --- a/avm/ptn/ai-platform/baseline/main.json +++ b/avm/ptn/ai-platform/baseline/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.30.3.12046", - "templateHash": "12442663604053803236" + "templateHash": "14736544493210620894" }, "name": "AI Platform Baseline", "description": "This module provides a secure and scalable environment for deploying AI applications on Azure.\nThe module encompasses all essential components required for building, managing, and observing AI solutions, including a machine learning workspace, observability tools, and necessary data management services.\nBy integrating with Microsoft Entra ID for secure identity management and utilizing private endpoints for services like Key Vault and Blob Storage, the module ensures secure communication and data access.", @@ -823,7 +823,6 @@ "createBastion": "[and(variables('createVirtualNetwork'), not(equals(tryGet(parameters('bastionConfiguration'), 'enabled'), false())))]", "createVirtualMachine": "[and(variables('createVirtualNetwork'), not(equals(tryGet(parameters('virtualMachineConfiguration'), 'enabled'), false())))]", "createDefaultNsg": "[equals(tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'networkSecurityGroupResourceId'), null())]", - "subnetResourceId": "[if(variables('createVirtualNetwork'), resourceId('Microsoft.Network/virtualNetworks/subnets', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))), coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'name'), 'default')), null())]", "mlTargetSubResource": "amlworkspace", "mlPrivateDnsZones": { "privatelink.api.azureml.ms": "[variables('mlTargetSubResource')]", @@ -835,36 +834,6 @@ } }, "resources": { - "virtualNetwork::defaultSubnet": { - "condition": "[variables('createVirtualNetwork')]", - "type": "Microsoft.Network/virtualNetworks/subnets", - "apiVersion": "2024-01-01", - "name": "[format('{0}/{1}', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))), coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'name'), 'default'))]", - "properties": { - "addressPrefix": "[coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'addressPrefix'), '10.0.0.0/24')]", - "networkSecurityGroup": { - "id": "[if(variables('createDefaultNsg'), reference('defaultNetworkSecurityGroup').outputs.resourceId.value, tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'networkSecurityGroupResourceId'))]" - } - }, - "dependsOn": [ - "defaultNetworkSecurityGroup", - "virtualNetwork" - ] - }, - "virtualNetwork::bastionSubnet": { - "condition": "[and(variables('createVirtualNetwork'), variables('createBastion'))]", - "type": "Microsoft.Network/virtualNetworks/subnets", - "apiVersion": "2024-01-01", - "name": "[format('{0}/{1}', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))), 'AzureBastionSubnet')]", - "properties": { - "addressPrefix": "[coalesce(tryGet(parameters('bastionConfiguration'), 'subnetAddressPrefix'), '10.0.1.0/26')]", - "networkSecurityGroup": "[if(not(equals(tryGet(parameters('bastionConfiguration'), 'networkSecurityGroupResourceId'), null())), createObject('id', tryGet(parameters('bastionConfiguration'), 'networkSecurityGroupResourceId')), null())]" - }, - "dependsOn": [ - "virtualNetwork::defaultSubnet", - "virtualNetwork" - ] - }, "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", @@ -885,21 +854,6 @@ } } }, - "virtualNetwork": { - "condition": "[variables('createVirtualNetwork')]", - "type": "Microsoft.Network/virtualNetworks", - "apiVersion": "2024-01-01", - "name": "[coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name')))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'addressPrefix'), '10.0.0.0/16')]" - ] - } - } - }, "userAssignedIdentity": { "condition": "[not(equals(parameters('managedIdentityName'), null()))]", "existing": true, @@ -949,7 +903,7 @@ "virtualNetworkLinks": { "value": [ { - "virtualNetworkResourceId": "[resourceId('Microsoft.Network/virtualNetworks', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))))]" + "virtualNetworkResourceId": "[reference('virtualNetwork').outputs.resourceId.value]" } ] } @@ -3831,7 +3785,7 @@ "virtualNetworkLinks": { "value": [ { - "virtualNetworkResourceId": "[resourceId('Microsoft.Network/virtualNetworks', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))))]" + "virtualNetworkResourceId": "[reference('virtualNetwork').outputs.resourceId.value]" } ] }, @@ -7235,61 +7189,1578 @@ "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" }, "dependsOn": [ - "networkSecurityGroup" + "networkSecurityGroup" + ] + }, + "networkSecurityGroup_diagnosticSettings": { + "copy": { + "name": "networkSecurityGroup_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + }, + "networkSecurityGroup_roleAssignments": { + "copy": { + "name": "networkSecurityGroup_roleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "name": "[guid(resourceId('Microsoft.Network/networkSecurityGroups', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "properties": { + "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "networkSecurityGroup" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the network security group was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the network security group." + }, + "value": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the network security group." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('networkSecurityGroup', '2023-11-01', 'full').location]" + } + } + } + } + }, + "virtualNetwork": { + "condition": "[variables('createVirtualNetwork')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-virtual-network', uniqueString(deployment().name, parameters('location')))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name')))]" + }, + "location": { + "value": "[parameters('location')]" + }, + "enableTelemetry": { + "value": "[parameters('enableTelemetry')]" + }, + "addressPrefixes": { + "value": [ + "[coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'addressPrefix'), '10.0.0.0/16')]" + ] + }, + "subnets": { + "value": "[union(createArray(createObject('addressPrefix', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'addressPrefix'), '10.0.0.0/24'), 'name', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'name'), 'default'), 'networkSecurityGroupResourceId', if(variables('createDefaultNsg'), reference('defaultNetworkSecurityGroup').outputs.resourceId.value, tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'networkSecurityGroupResourceId')))), if(variables('createBastion'), createArray(createObject('addressPrefix', coalesce(tryGet(parameters('bastionConfiguration'), 'subnetAddressPrefix'), '10.0.1.0/26'), 'name', 'AzureBastionSubnet', 'networkSecurityGroupResourceId', tryGet(parameters('bastionConfiguration'), 'networkSecurityGroupResourceId'))), createArray()))]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "15949466154563447171" + }, + "name": "Virtual Networks", + "description": "This module deploys a Virtual Network (vNet).", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + }, + "peeringType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be peer-localVnetName-remoteVnetName." + } + }, + "remoteVirtualNetworkResourceId": { + "type": "string", + "metadata": { + "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." + } + }, + "allowForwardedTraffic": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "allowGatewayTransit": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Do not verify the provisioning state of the remote gateway. Default is true." + } + }, + "useRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "remotePeeringEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Deploy the outbound and the inbound peering." + } + }, + "remotePeeringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the VNET Peering resource in the remove Virtual Network. If not provided, default value will be peer-remoteVnetName-localVnetName." + } + }, + "remotePeeringAllowForwardedTraffic": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "remotePeeringAllowGatewayTransit": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "remotePeeringAllowVirtualNetworkAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "remotePeeringDoNotVerifyRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Do not verify the provisioning state of the remote gateway. Default is true." + } + }, + "remotePeeringUseRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + } + } + }, + "subnetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The Name of the subnet resource." + } + }, + "addressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." + } + }, + "applicationGatewayIPConfigurations": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application gateway IP configurations of virtual network resource." + } + }, + "delegation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The delegation to enable on the subnet." + } + }, + "natGatewayResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the network security group to assign to the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "allowedValues": [ + "", + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "allowedValues": [ + "", + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. enable or disable apply network policies on private link service in the subnet." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "routeTableResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "serviceEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + }, + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty." + } + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Virtual Network (vNet)." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all resources." + } + }, + "addressPrefixes": { + "type": "array", + "metadata": { + "description": "Required. An Array of 1 or more IP Address Prefixes for the Virtual Network." + } + }, + "virtualNetworkBgpCommunity": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The BGP community associated with the virtual network." + } + }, + "subnets": { + "type": "array", + "items": { + "$ref": "#/definitions/subnetType" + }, + "nullable": true, + "metadata": { + "description": "Optional. An Array of subnets to deploy to the Virtual Network." + } + }, + "dnsServers": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. DNS Servers associated to the Virtual Network." + } + }, + "ddosProtectionPlanResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "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." + } + }, + "peerings": { + "type": "array", + "items": { + "$ref": "#/definitions/peeringType" + }, + "nullable": true, + "metadata": { + "description": "Optional. Virtual Network Peering configurations." + } + }, + "vnetEncryption": { + "type": "bool", + "defaultValue": false, + "metadata": { + "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." + } + }, + "vnetEncryptionEnforcement": { + "type": "string", + "defaultValue": "AllowUnencrypted", + "allowedValues": [ + "AllowUnencrypted", + "DropUnencrypted" + ], + "metadata": { + "description": "Optional. If the encrypted VNet allows VM that does not support encryption. Can only be used when vnetEncryption is enabled." + } + }, + "flowTimeoutInMinutes": { + "type": "int", + "defaultValue": 0, + "maxValue": 30, + "metadata": { + "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." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "enableVmProtection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Indicates if VM protection is enabled for all the subnets in the virtual network." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-virtualnetwork.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "virtualNetwork": { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2024-01-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "addressSpace": { + "addressPrefixes": "[parameters('addressPrefixes')]" + }, + "bgpCommunities": "[if(not(empty(parameters('virtualNetworkBgpCommunity'))), createObject('virtualNetworkCommunity', parameters('virtualNetworkBgpCommunity')), null())]", + "ddosProtectionPlan": "[if(not(empty(parameters('ddosProtectionPlanResourceId'))), createObject('id', parameters('ddosProtectionPlanResourceId')), null())]", + "dhcpOptions": "[if(not(empty(parameters('dnsServers'))), createObject('dnsServers', array(parameters('dnsServers'))), null())]", + "enableDdosProtection": "[not(empty(parameters('ddosProtectionPlanResourceId')))]", + "encryption": "[if(equals(parameters('vnetEncryption'), true()), createObject('enabled', parameters('vnetEncryption'), 'enforcement', parameters('vnetEncryptionEnforcement')), null())]", + "flowTimeoutInMinutes": "[if(not(equals(parameters('flowTimeoutInMinutes'), 0)), parameters('flowTimeoutInMinutes'), null())]", + "enableVmProtection": "[parameters('enableVmProtection')]" + } + }, + "virtualNetwork_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_diagnosticSettings": { + "copy": { + "name": "virtualNetwork_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_roleAssignments": { + "copy": { + "name": "virtualNetwork_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "virtualNetwork_subnets": { + "copy": { + "name": "virtualNetwork_subnets", + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "mode": "serial", + "batchSize": 1 + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-subnet-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "virtualNetworkName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('subnets'), createArray())[copyIndex()].name]" + }, + "addressPrefix": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'addressPrefix')]" + }, + "addressPrefixes": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'addressPrefixes')]" + }, + "applicationGatewayIPConfigurations": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'applicationGatewayIPConfigurations')]" + }, + "delegation": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'delegation')]" + }, + "natGatewayResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'natGatewayResourceId')]" + }, + "networkSecurityGroupResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'networkSecurityGroupResourceId')]" + }, + "privateEndpointNetworkPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'privateEndpointNetworkPolicies')]" + }, + "privateLinkServiceNetworkPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'privateLinkServiceNetworkPolicies')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "routeTableResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'routeTableResourceId')]" + }, + "serviceEndpointPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'serviceEndpointPolicies')]" + }, + "serviceEndpoints": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'serviceEndpoints')]" + }, + "defaultOutboundAccess": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'defaultOutboundAccess')]" + }, + "sharingScope": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'sharingScope')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5699372618313647761" + }, + "name": "Virtual Network Subnets", + "description": "This module deploys a Virtual Network Subnet.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Requird. The Name of the subnet resource." + } + }, + "virtualNetworkName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent virtual network. Required if the template is used in a standalone deployment." + } + }, + "addressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the network security group to assign to the subnet." + } + }, + "routeTableResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "defaultValue": [], + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "delegation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The delegation to enable on the subnet." + } + }, + "natGatewayResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "Disabled", + "Enabled", + "" + ], + "metadata": { + "description": "Optional. Enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "defaultValue": "", + "allowedValues": [ + "Disabled", + "Enabled", + "" + ], + "metadata": { + "description": "Optional. Enable or disable apply network policies on private link service in the subnet." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." + } + }, + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + }, + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty." + } + }, + "applicationGatewayIPConfigurations": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. Application gateway IP configurations of virtual network resource." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "virtualNetwork": { + "existing": true, + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2024-01-01", + "name": "[parameters('virtualNetworkName')]" + }, + "subnet": { + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "properties": { + "copy": [ + { + "name": "serviceEndpoints", + "count": "[length(parameters('serviceEndpoints'))]", + "input": { + "service": "[parameters('serviceEndpoints')[copyIndex('serviceEndpoints')]]" + } + } + ], + "addressPrefix": "[parameters('addressPrefix')]", + "addressPrefixes": "[parameters('addressPrefixes')]", + "networkSecurityGroup": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('id', parameters('networkSecurityGroupResourceId')), null())]", + "routeTable": "[if(not(empty(parameters('routeTableResourceId'))), createObject('id', parameters('routeTableResourceId')), null())]", + "natGateway": "[if(not(empty(parameters('natGatewayResourceId'))), createObject('id', parameters('natGatewayResourceId')), null())]", + "delegations": "[if(not(empty(parameters('delegation'))), createArray(createObject('name', parameters('delegation'), 'properties', createObject('serviceName', parameters('delegation')))), createArray())]", + "privateEndpointNetworkPolicies": "[if(not(empty(parameters('privateEndpointNetworkPolicies'))), parameters('privateEndpointNetworkPolicies'), null())]", + "privateLinkServiceNetworkPolicies": "[if(not(empty(parameters('privateLinkServiceNetworkPolicies'))), parameters('privateLinkServiceNetworkPolicies'), null())]", + "applicationGatewayIPConfigurations": "[parameters('applicationGatewayIPConfigurations')]", + "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]", + "defaultOutboundAccess": "[parameters('defaultOutboundAccess')]", + "sharingScope": "[parameters('sharingScope')]" + }, + "dependsOn": [ + "virtualNetwork" + ] + }, + "subnet_roleAssignments": { + "copy": { + "name": "subnet_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}/subnets/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "subnet" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name'))]" + }, + "addressPrefix": { + "type": "string", + "metadata": { + "description": "The address prefix for the subnet." + }, + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefix'), '')]" + }, + "addressPrefixes": { + "type": "array", + "metadata": { + "description": "List of address prefixes for the subnet." + }, + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefixes'), createArray())]" + } + } + } + }, + "dependsOn": [ + "virtualNetwork" ] }, - "networkSecurityGroup_diagnosticSettings": { + "virtualNetwork_peering_local": { "copy": { - "name": "networkSecurityGroup_diagnosticSettings", - "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + "name": "virtualNetwork_peering_local", + "count": "[length(coalesce(parameters('peerings'), createArray()))]" }, - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-virtualNetworkPeering-local-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { - "copy": [ - { - "name": "logs", - "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", - "input": { - "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", - "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", - "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "localVnetName": { + "value": "[parameters('name')]" + }, + "remoteVirtualNetworkResourceId": { + "value": "[coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId]" + }, + "name": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'name')]" + }, + "allowForwardedTraffic": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowForwardedTraffic')]" + }, + "allowGatewayTransit": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowGatewayTransit')]" + }, + "allowVirtualNetworkAccess": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowVirtualNetworkAccess')]" + }, + "doNotVerifyRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'doNotVerifyRemoteGateways')]" + }, + "useRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'useRemoteGateways')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5206620163504251868" + }, + "name": "Virtual Network Peerings", + "description": "This module deploys a Virtual Network Peering.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + } + }, + "localVnetName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." + } + }, + "remoteVirtualNetworkResourceId": { + "type": "string", + "metadata": { + "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." + } + }, + "allowForwardedTraffic": { + "type": "bool", + "defaultValue": true, + "metadata": { + "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." + } + }, + "allowGatewayTransit": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "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." + } + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. If we need to verify the provisioning state of the remote gateway. Default is true." + } + }, + "useRemoteGateways": { + "type": "bool", + "defaultValue": false, + "metadata": { + "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." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", + "properties": { + "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", + "allowGatewayTransit": "[parameters('allowGatewayTransit')]", + "allowVirtualNetworkAccess": "[parameters('allowVirtualNetworkAccess')]", + "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", + "useRemoteGateways": "[parameters('useRemoteGateways')]", + "remoteVirtualNetwork": { + "id": "[parameters('remoteVirtualNetworkResourceId')]" + } + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/virtualNetworkPeerings', parameters('localVnetName'), parameters('name'))]" } } - ], - "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", - "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", - "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", - "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", - "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", - "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + } }, "dependsOn": [ - "networkSecurityGroup" + "virtualNetwork" ] }, - "networkSecurityGroup_roleAssignments": { + "virtualNetwork_peering_remote": { "copy": { - "name": "networkSecurityGroup_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "name": "virtualNetwork_peering_remote", + "count": "[length(coalesce(parameters('peerings'), createArray()))]" }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/networkSecurityGroups', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "condition": "[coalesce(tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringEnabled'), false())]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-virtualNetworkPeering-remote-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "subscriptionId": "[split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/')[2]]", + "resourceGroup": "[split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/')[4]]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "localVnetName": { + "value": "[last(split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/'))]" + }, + "remoteVirtualNetworkResourceId": { + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" + }, + "name": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringName')]" + }, + "allowForwardedTraffic": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowForwardedTraffic')]" + }, + "allowGatewayTransit": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowGatewayTransit')]" + }, + "allowVirtualNetworkAccess": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowVirtualNetworkAccess')]" + }, + "doNotVerifyRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringDoNotVerifyRemoteGateways')]" + }, + "useRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringUseRemoteGateways')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5206620163504251868" + }, + "name": "Virtual Network Peerings", + "description": "This module deploys a Virtual Network Peering.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]", + "metadata": { + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + } + }, + "localVnetName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." + } + }, + "remoteVirtualNetworkResourceId": { + "type": "string", + "metadata": { + "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." + } + }, + "allowForwardedTraffic": { + "type": "bool", + "defaultValue": true, + "metadata": { + "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." + } + }, + "allowGatewayTransit": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "defaultValue": true, + "metadata": { + "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." + } + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. If we need to verify the provisioning state of the remote gateway. Default is true." + } + }, + "useRemoteGateways": { + "type": "bool", + "defaultValue": false, + "metadata": { + "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." + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2024-01-01", + "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", + "properties": { + "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", + "allowGatewayTransit": "[parameters('allowGatewayTransit')]", + "allowVirtualNetworkAccess": "[parameters('allowVirtualNetworkAccess')]", + "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", + "useRemoteGateways": "[parameters('useRemoteGateways')]", + "remoteVirtualNetwork": { + "id": "[parameters('remoteVirtualNetworkResourceId')]" + } + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the virtual network peering was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the virtual network peering." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the virtual network peering." + }, + "value": "[resourceId('Microsoft.Network/virtualNetworks/virtualNetworkPeerings', parameters('localVnetName'), parameters('name'))]" + } + } + } }, "dependsOn": [ - "networkSecurityGroup" + "virtualNetwork" ] } }, @@ -7297,34 +8768,57 @@ "resourceGroupName": { "type": "string", "metadata": { - "description": "The resource group the network security group was deployed into." + "description": "The resource group the virtual network was deployed into." }, "value": "[resourceGroup().name]" }, "resourceId": { "type": "string", "metadata": { - "description": "The resource ID of the network security group." + "description": "The resource ID of the virtual network." }, - "value": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('name'))]" + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" }, "name": { "type": "string", "metadata": { - "description": "The name of the network security group." + "description": "The name of the virtual network." }, "value": "[parameters('name')]" }, + "subnetNames": { + "type": "array", + "metadata": { + "description": "The names of the deployed subnets." + }, + "copy": { + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "input": "[reference(format('virtualNetwork_subnets[{0}]', copyIndex())).outputs.name.value]" + } + }, + "subnetResourceIds": { + "type": "array", + "metadata": { + "description": "The resource IDs of the deployed subnets." + }, + "copy": { + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "input": "[reference(format('virtualNetwork_subnets[{0}]', copyIndex())).outputs.resourceId.value]" + } + }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('networkSecurityGroup', '2023-11-01', 'full').location]" + "value": "[reference('virtualNetwork', '2024-01-01', 'full').location]" } } } - } + }, + "dependsOn": [ + "defaultNetworkSecurityGroup" + ] }, "bastion": { "condition": "[variables('createBastion')]", @@ -7350,7 +8844,7 @@ "value": "[parameters('enableTelemetry')]" }, "virtualNetworkResourceId": { - "value": "[resourceId('Microsoft.Network/virtualNetworks', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))))]" + "value": "[reference('virtualNetwork').outputs.resourceId.value]" }, "disableCopyPaste": { "value": "[tryGet(parameters('bastionConfiguration'), 'disableCopyPaste')]" @@ -8549,7 +10043,7 @@ { "name": "[coalesce(tryGet(parameters('virtualMachineConfiguration'), 'nicConfigurationConfiguration', 'ipConfigName'), format('nic-vm-{0}-ipconfig', parameters('name')))]", "privateIPAllocationMethod": "[coalesce(tryGet(parameters('virtualMachineConfiguration'), 'nicConfigurationConfiguration', 'privateIPAllocationMethod'), 'Dynamic')]", - "subnetResourceId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))), coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'name'), 'default'))]" + "subnetResourceId": "[reference('virtualNetwork').outputs.subnetResourceIds.value[0]]" } ] } @@ -13968,7 +15462,7 @@ } }, "dependsOn": [ - "virtualNetwork::defaultSubnet" + "virtualNetwork" ] }, "keyVault": { @@ -16805,7 +18299,7 @@ "bypass": "AzureServices" } }, - "privateEndpoints": "[if(not(equals(variables('subnetResourceId'), null())), createObject('value', map(items(variables('storagePrivateDnsZones')), lambda('zone', createObject('name', format('pep-{0}-{1}', lambdaVariables('zone').value, parameters('name')), 'customNetworkInterfaceName', format('nic-{0}-{1}', lambdaVariables('zone').value, parameters('name')), 'service', lambdaVariables('zone').value, 'subnetResourceId', coalesce(variables('subnetResourceId'), ''), 'privateDnsZoneResourceIds', createArray(resourceId('Microsoft.Network/privateDnsZones', lambdaVariables('zone').key)))))), createObject('value', null()))]", + "privateEndpoints": "[if(not(equals(if(variables('createVirtualNetwork'), reference('virtualNetwork').outputs.subnetResourceIds.value[0], null()), null())), createObject('value', map(items(variables('storagePrivateDnsZones')), lambda('zone', createObject('name', format('pep-{0}-{1}', lambdaVariables('zone').value, parameters('name')), 'customNetworkInterfaceName', format('nic-{0}-{1}', lambdaVariables('zone').value, parameters('name')), 'service', lambdaVariables('zone').value, 'subnetResourceId', coalesce(if(variables('createVirtualNetwork'), reference('virtualNetwork').outputs.subnetResourceIds.value[0], null()), ''), 'privateDnsZoneResourceIds', createArray(resourceId('Microsoft.Network/privateDnsZones', lambdaVariables('zone').key)))))), createObject('value', null()))]", "roleAssignments": "[if(not(equals(parameters('managedIdentityName'), null())), createObject('value', createArray(createObject('principalId', reference('userAssignedIdentity').principalId, 'roleDefinitionIdOrName', 'Contributor', 'principalType', 'ServicePrincipal'), createObject('principalId', reference('userAssignedIdentity').principalId, 'roleDefinitionIdOrName', 'Storage Blob Data Contributor', 'principalType', 'ServicePrincipal'), createObject('principalId', reference('userAssignedIdentity').principalId, 'roleDefinitionIdOrName', '69566ab7-960f-475b-8e7c-b3118f30c6bd', 'principalType', 'ServicePrincipal'))), createObject('value', null()))]", "tags": { "value": "[parameters('tags')]" @@ -21469,9 +22963,9 @@ } }, "dependsOn": [ - "virtualNetwork::defaultSubnet", "storageAccount_privateDnsZones", - "userAssignedIdentity" + "userAssignedIdentity", + "virtualNetwork" ] }, "containerRegistry": { @@ -24315,7 +25809,7 @@ "outboundRules": "[tryGet(parameters('workspaceConfiguration'), 'networkOutboundRules')]" } }, - "privateEndpoints": "[if(not(equals(variables('subnetResourceId'), null())), createObject('value', createArray(createObject('name', format('pep-{0}-{1}', variables('mlTargetSubResource'), parameters('name')), 'customNetworkInterfaceName', format('nic-{0}-{1}', variables('mlTargetSubResource'), parameters('name')), 'service', variables('mlTargetSubResource'), 'subnetResourceId', coalesce(variables('subnetResourceId'), ''), 'privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', map(objectKeys(variables('mlPrivateDnsZones')), lambda('zone', createObject('name', replace(lambdaVariables('zone'), '.', '-'), 'privateDnsZoneResourceId', resourceId('Microsoft.Network/privateDnsZones', lambdaVariables('zone'))))))))), createObject('value', null()))]", + "privateEndpoints": "[if(not(equals(if(variables('createVirtualNetwork'), reference('virtualNetwork').outputs.subnetResourceIds.value[0], null()), null())), createObject('value', createArray(createObject('name', format('pep-{0}-{1}', variables('mlTargetSubResource'), parameters('name')), 'customNetworkInterfaceName', format('nic-{0}-{1}', variables('mlTargetSubResource'), parameters('name')), 'service', variables('mlTargetSubResource'), 'subnetResourceId', coalesce(if(variables('createVirtualNetwork'), reference('virtualNetwork').outputs.subnetResourceIds.value[0], null()), ''), 'privateDnsZoneGroup', createObject('privateDnsZoneGroupConfigs', map(objectKeys(variables('mlPrivateDnsZones')), lambda('zone', createObject('name', replace(lambdaVariables('zone'), '.', '-'), 'privateDnsZoneResourceId', resourceId('Microsoft.Network/privateDnsZones', lambdaVariables('zone'))))))))), createObject('value', null()))]", "systemDatastoresAuthMode": { "value": "identity" }, @@ -26563,10 +28057,10 @@ "dependsOn": [ "applicationInsights", "containerRegistry", - "virtualNetwork::defaultSubnet", "keyVault", "storageAccount", "userAssignedIdentity", + "virtualNetwork", "workspaceHub_privateDnsZones" ] }, @@ -29005,28 +30499,28 @@ "metadata": { "description": "The resource ID of the virtual network." }, - "value": "[if(variables('createVirtualNetwork'), resourceId('Microsoft.Network/virtualNetworks', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name')))), '')]" + "value": "[if(variables('createVirtualNetwork'), reference('virtualNetwork').outputs.resourceId.value, '')]" }, "virtualNetworkName": { "type": "string", "metadata": { "description": "The name of the virtual network." }, - "value": "[if(variables('createVirtualNetwork'), coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))), '')]" + "value": "[if(variables('createVirtualNetwork'), reference('virtualNetwork').outputs.name.value, '')]" }, "virtualNetworkSubnetResourceId": { "type": "string", "metadata": { "description": "The resource ID of the subnet in the virtual network." }, - "value": "[if(variables('createVirtualNetwork'), resourceId('Microsoft.Network/virtualNetworks/subnets', coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'name'), format('vnet-{0}', parameters('name'))), coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'name'), 'default')), '')]" + "value": "[if(variables('createVirtualNetwork'), reference('virtualNetwork').outputs.subnetResourceIds.value[0], '')]" }, "virtualNetworkSubnetName": { "type": "string", "metadata": { "description": "The name of the subnet in the virtual network." }, - "value": "[if(variables('createVirtualNetwork'), coalesce(tryGet(parameters('virtualNetworkConfiguration'), 'subnet', 'name'), 'default'), '')]" + "value": "[if(variables('createVirtualNetwork'), reference('virtualNetwork').outputs.subnetNames.value[0], '')]" }, "bastionResourceId": { "type": "string", diff --git a/avm/ptn/ai-platform/baseline/version.json b/avm/ptn/ai-platform/baseline/version.json index 41fc8c654f..c332ff1f3a 100644 --- a/avm/ptn/ai-platform/baseline/version.json +++ b/avm/ptn/ai-platform/baseline/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] From 51596f2d87a24e51f0ef8bf37e7b8f2af5d23d79 Mon Sep 17 00:00:00 2001 From: Chinedum Echeta <60179183+cecheta@users.noreply.github.com> Date: Wed, 25 Sep 2024 02:40:38 -0700 Subject: [PATCH 35/57] docs: `avm/ptn/ai-platform/baseline` Update README (#3358) ## Description Updating the README, which changed since the last PR was raised ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.ai-platform.baseline](https://github.com/cecheta/bicep-registry-modules/actions/workflows/avm.ptn.ai-platform.baseline.yml/badge.svg?branch=readme)](https://github.com/cecheta/bicep-registry-modules/actions/workflows/avm.ptn.ai-platform.baseline.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/ptn/ai-platform/baseline/README.md | 12 ++++++------ avm/ptn/ai-platform/baseline/main.json | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/avm/ptn/ai-platform/baseline/README.md b/avm/ptn/ai-platform/baseline/README.md index 39a480afb9..709ebd90fe 100644 --- a/avm/ptn/ai-platform/baseline/README.md +++ b/avm/ptn/ai-platform/baseline/README.md @@ -65,14 +65,14 @@ By integrating with Microsoft Entra ID for secure identity management and utiliz | `Microsoft.Storage/storageAccounts/blobServices` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices) | | `Microsoft.Storage/storageAccounts/blobServices/containers` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers) | | `Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers/immutabilityPolicies) | -| `Microsoft.Storage/storageAccounts/fileServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/fileServices) | +| `Microsoft.Storage/storageAccounts/fileServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/fileServices) | | `Microsoft.Storage/storageAccounts/fileServices/shares` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-01-01/storageAccounts/fileServices/shares) | -| `Microsoft.Storage/storageAccounts/localUsers` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/localUsers) | +| `Microsoft.Storage/storageAccounts/localUsers` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/localUsers) | | `Microsoft.Storage/storageAccounts/managementPolicies` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-01-01/storageAccounts/managementPolicies) | -| `Microsoft.Storage/storageAccounts/queueServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/queueServices) | -| `Microsoft.Storage/storageAccounts/queueServices/queues` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/queueServices/queues) | -| `Microsoft.Storage/storageAccounts/tableServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/tableServices) | -| `Microsoft.Storage/storageAccounts/tableServices/tables` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/tableServices/tables) | +| `Microsoft.Storage/storageAccounts/queueServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/queueServices) | +| `Microsoft.Storage/storageAccounts/queueServices/queues` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/queueServices/queues) | +| `Microsoft.Storage/storageAccounts/tableServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/tableServices) | +| `Microsoft.Storage/storageAccounts/tableServices/tables` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/tableServices/tables) | ## Usage examples diff --git a/avm/ptn/ai-platform/baseline/main.json b/avm/ptn/ai-platform/baseline/main.json index 9da789cd5f..cbb20a89d4 100644 --- a/avm/ptn/ai-platform/baseline/main.json +++ b/avm/ptn/ai-platform/baseline/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.3.12046", - "templateHash": "14736544493210620894" + "version": "0.30.23.60470", + "templateHash": "14190804454906731332" }, "name": "AI Platform Baseline", "description": "This module provides a secure and scalable environment for deploying AI applications on Azure.\nThe module encompasses all essential components required for building, managing, and observing AI solutions, including a machine learning workspace, observability tools, and necessary data management services.\nBy integrating with Microsoft Entra ID for secure identity management and utilizing private endpoints for services like Key Vault and Blob Storage, the module ensures secure communication and data access.", From d213e8a8a0149f9ad72cfb037b0917af552d6af1 Mon Sep 17 00:00:00 2001 From: Nate Arnold Date: Wed, 25 Sep 2024 05:57:42 -0600 Subject: [PATCH 36/57] fix: Added depends_on to flexibleserver_administrators (#3355) ## Description Fixes #3257 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.db-for-postgre-sql.flexible-server](https://github.com/arnoldna/bicep-registry-modules/actions/workflows/avm.res.db-for-postgre-sql.flexible-server.yml/badge.svg?branch=avm%2Fres%2Fdb-for-postgre-sql%2Fflexible-server2)](https://github.com/arnoldna/bicep-registry-modules/actions/workflows/avm.res.db-for-postgre-sql.flexible-server.yml) | ## Type of Change - [X] Update to CI Environment or utilities (Non-module affecting changes) - [X] Azure Verified Module updates: - [X] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [X] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/db-for-postgre-sql/flexible-server/main.bicep | 3 +++ avm/res/db-for-postgre-sql/flexible-server/main.json | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/avm/res/db-for-postgre-sql/flexible-server/main.bicep b/avm/res/db-for-postgre-sql/flexible-server/main.bicep index 19c07e2e2f..7b24eb2bc7 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/main.bicep +++ b/avm/res/db-for-postgre-sql/flexible-server/main.bicep @@ -377,6 +377,9 @@ module flexibleServer_administrators 'administrator/main.bicep' = [ principalType: administrator.principalType tenantId: administrator.?tenantId ?? tenant().tenantId } + dependsOn: [ + flexibleServer_configurations + ] } ] diff --git a/avm/res/db-for-postgre-sql/flexible-server/main.json b/avm/res/db-for-postgre-sql/flexible-server/main.json index 9472f5d8f9..3e71af1f04 100644 --- a/avm/res/db-for-postgre-sql/flexible-server/main.json +++ b/avm/res/db-for-postgre-sql/flexible-server/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "745168326315156090" + "templateHash": "16193893481581771669" }, "name": "DBforPostgreSQL Flexible Servers", "description": "This module deploys a DBforPostgreSQL Flexible Server.", @@ -1193,7 +1193,8 @@ } }, "dependsOn": [ - "flexibleServer" + "flexibleServer", + "flexibleServer_configurations" ] } }, From 5fc18d3c5929f8cb031b371bf6974d55b1e803c0 Mon Sep 17 00:00:00 2001 From: hundredacres Date: Wed, 25 Sep 2024 09:06:49 -0700 Subject: [PATCH 37/57] fix: Resolve issue when bastion/firewall is not deployed (#3356) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Resolves issue when bastion and firewall are not deployed. Added test to confirm. Fixes #3343 Closes #3343 ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.network.hub-networking](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.ptn.network.hub-networking.yml/badge.svg?branch=fix%2Fissues%2F3343)](https://github.com/hundredacres/bicep-registry-modules/actions/workflows/avm.ptn.network.hub-networking.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [X] Azure Verified Module updates: - [X] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [X] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Máté Barabás Co-authored-by: Rainer Halanek <61878316+rahalan@users.noreply.github.com> Co-authored-by: JFolberth --- avm/ptn/network/hub-networking/README.md | 173 +++++++++++++++++- avm/ptn/network/hub-networking/main.bicep | 28 +-- avm/ptn/network/hub-networking/main.json | 30 +-- .../tests/e2e/no-addons/main.test.bicep | 124 +++++++++++++ 4 files changed, 321 insertions(+), 34 deletions(-) create mode 100644 avm/ptn/network/hub-networking/tests/e2e/no-addons/main.test.bicep diff --git a/avm/ptn/network/hub-networking/README.md b/avm/ptn/network/hub-networking/README.md index 9dd17dbb8d..e033907b82 100644 --- a/avm/ptn/network/hub-networking/README.md +++ b/avm/ptn/network/hub-networking/README.md @@ -38,7 +38,8 @@ The following section provides usage examples for the module, which were used to - [Using only defaults](#example-1-using-only-defaults) - [Using large parameter set](#example-2-using-large-parameter-set) -- [WAF-aligned](#example-3-waf-aligned) +- [No Addons](#example-3-no-addons) +- [WAF-aligned](#example-4-waf-aligned) ### Example 1: _Using only defaults_ @@ -448,7 +449,175 @@ module hubNetworking 'br/public:avm/ptn/network/hub-networking:' = {

-### Example 3: _WAF-aligned_ +### Example 3: _No Addons_ + +This instance deploys the module with no add-ons (Firewall / Bastion) enabled. + + +

+ +via Bicep module + +```bicep +module hubNetworking 'br/public:avm/ptn/network/hub-networking:' = { + name: 'hubNetworkingDeployment' + params: { + hubVirtualNetworks: { + hub1: { + addressPrefixes: '' + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + dnsServers: [ + '10.0.1.6' + '10.0.1.7' + ] + enableAzureFirewall: false + enableBastion: false + enablePeering: false + enableTelemetry: true + flowTimeoutInMinutes: 30 + location: '' + lock: { + kind: 'CanNotDelete' + name: 'hub1Lock' + } + routes: [ + { + name: 'defaultRoute' + properties: { + addressPrefix: '0.0.0.0/0' + nextHopType: 'Internet' + } + } + ] + subnets: [ + { + addressPrefix: '' + name: 'GatewaySubnet' + } + { + addressPrefix: '' + name: 'AzureFirewallSubnet' + } + { + addressPrefix: '' + name: 'AzureBastionSubnet' + } + ] + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + vnetEncryption: false + vnetEncryptionEnforcement: 'AllowUnencrypted' + } + } + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "hubVirtualNetworks": { + "value": { + "hub1": { + "addressPrefixes": "", + "diagnosticSettings": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ], + "dnsServers": [ + "10.0.1.6", + "10.0.1.7" + ], + "enableAzureFirewall": false, + "enableBastion": false, + "enablePeering": false, + "enableTelemetry": true, + "flowTimeoutInMinutes": 30, + "location": "", + "lock": { + "kind": "CanNotDelete", + "name": "hub1Lock" + }, + "routes": [ + { + "name": "defaultRoute", + "properties": { + "addressPrefix": "0.0.0.0/0", + "nextHopType": "Internet" + } + } + ], + "subnets": [ + { + "addressPrefix": "", + "name": "GatewaySubnet" + }, + { + "addressPrefix": "", + "name": "AzureFirewallSubnet" + }, + { + "addressPrefix": "", + "name": "AzureBastionSubnet" + } + ], + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + }, + "vnetEncryption": false, + "vnetEncryptionEnforcement": "AllowUnencrypted" + } + } + }, + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 4: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. diff --git a/avm/ptn/network/hub-networking/main.bicep b/avm/ptn/network/hub-networking/main.bicep index 981cdd4eff..2f222c6bc0 100644 --- a/avm/ptn/network/hub-networking/main.bicep +++ b/avm/ptn/network/hub-networking/main.bicep @@ -245,22 +245,26 @@ output hubVirtualNetworks object[] = [ @description('Array of hub bastion resources.') output hubBastions object[] = [ - for (hub, index) in items(hubVirtualNetworks ?? {}): { - resourceGroupName: hubBastion[index].outputs.resourceGroupName - location: hubBastion[index].outputs.location - name: hubBastion[index].outputs.name - resourceId: hubBastion[index].outputs.resourceId - } + for (hub, index) in items(hubVirtualNetworks ?? {}): (hub.value.enableBastion) + ? { + resourceGroupName: hubBastion[index].outputs.resourceGroupName + location: hubBastion[index].outputs.location + name: hubBastion[index].outputs.name + resourceId: hubBastion[index].outputs.resourceId + } + : {} ] @description('Array of hub Azure Firewall resources.') output hubAzureFirewalls object[] = [ - for (hub, index) in items(hubVirtualNetworks ?? {}): { - resourceGroupName: hubAzureFirewall[index].outputs.resourceGroupName - location: hubAzureFirewall[index].outputs.location - name: hubAzureFirewall[index].outputs.name - resourceId: hubAzureFirewall[index].outputs.resourceId - } + for (hub, index) in items(hubVirtualNetworks ?? {}): (hub.value.enableAzureFirewall) + ? { + resourceGroupName: hubAzureFirewall[index].outputs.resourceGroupName + location: hubAzureFirewall[index].outputs.location + name: hubAzureFirewall[index].outputs.name + resourceId: hubAzureFirewall[index].outputs.resourceId + } + : {} ] @description('The subnets of the hub virtual network.') diff --git a/avm/ptn/network/hub-networking/main.json b/avm/ptn/network/hub-networking/main.json index 8fb5cb887b..6cac966757 100644 --- a/avm/ptn/network/hub-networking/main.json +++ b/avm/ptn/network/hub-networking/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "9188161100861636713" + "version": "0.30.3.12046", + "templateHash": "8969613921663763778" }, "name": "Hub Networking", "description": "This module is designed to simplify the creation of multi-region hub networks in Azure. It will create a number of virtual networks and subnets, and optionally peer them together in a mesh topology with routing.", @@ -2315,8 +2315,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15958982442955537466" + "version": "0.30.3.12046", + "templateHash": "5568850224456572684" }, "name": "Virtual Networks", "description": "This module deploys a Virtual Network.", @@ -6436,8 +6436,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "13190798974838698070" + "version": "0.30.3.12046", + "templateHash": "16563975082451649304" }, "name": "Existing Virtual Network Subnets", "description": "This module retrieves an existing Virtual Network Subnet.", @@ -6519,8 +6519,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11735652948112662202" + "version": "0.30.3.12046", + "templateHash": "10294962787410461549" }, "name": "Virtual Network Subnets", "description": "This module deploys a Virtual Network Subnet.", @@ -6859,12 +6859,7 @@ }, "copy": { "count": "[length(items(coalesce(parameters('hubVirtualNetworks'), createObject())))]", - "input": { - "resourceGroupName": "[reference(format('hubBastion[{0}]', copyIndex())).outputs.resourceGroupName.value]", - "location": "[reference(format('hubBastion[{0}]', copyIndex())).outputs.location.value]", - "name": "[reference(format('hubBastion[{0}]', copyIndex())).outputs.name.value]", - "resourceId": "[reference(format('hubBastion[{0}]', copyIndex())).outputs.resourceId.value]" - } + "input": "[if(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value.enableBastion, createObject('resourceGroupName', reference(format('hubBastion[{0}]', copyIndex())).outputs.resourceGroupName.value, 'location', reference(format('hubBastion[{0}]', copyIndex())).outputs.location.value, 'name', reference(format('hubBastion[{0}]', copyIndex())).outputs.name.value, 'resourceId', reference(format('hubBastion[{0}]', copyIndex())).outputs.resourceId.value), createObject())]" } }, "hubAzureFirewalls": { @@ -6877,12 +6872,7 @@ }, "copy": { "count": "[length(items(coalesce(parameters('hubVirtualNetworks'), createObject())))]", - "input": { - "resourceGroupName": "[reference(format('hubAzureFirewall[{0}]', copyIndex())).outputs.resourceGroupName.value]", - "location": "[reference(format('hubAzureFirewall[{0}]', copyIndex())).outputs.location.value]", - "name": "[reference(format('hubAzureFirewall[{0}]', copyIndex())).outputs.name.value]", - "resourceId": "[reference(format('hubAzureFirewall[{0}]', copyIndex())).outputs.resourceId.value]" - } + "input": "[if(items(coalesce(parameters('hubVirtualNetworks'), createObject()))[copyIndex()].value.enableAzureFirewall, createObject('resourceGroupName', reference(format('hubAzureFirewall[{0}]', copyIndex())).outputs.resourceGroupName.value, 'location', reference(format('hubAzureFirewall[{0}]', copyIndex())).outputs.location.value, 'name', reference(format('hubAzureFirewall[{0}]', copyIndex())).outputs.name.value, 'resourceId', reference(format('hubAzureFirewall[{0}]', copyIndex())).outputs.resourceId.value), createObject())]" } }, "hubVirtualNetworkSubnets": { diff --git a/avm/ptn/network/hub-networking/tests/e2e/no-addons/main.test.bicep b/avm/ptn/network/hub-networking/tests/e2e/no-addons/main.test.bicep new file mode 100644 index 0000000000..12d7a9613a --- /dev/null +++ b/avm/ptn/network/hub-networking/tests/e2e/no-addons/main.test.bicep @@ -0,0 +1,124 @@ +targetScope = 'subscription' + +metadata name = 'No Addons' +metadata description = 'This instance deploys the module with no add-ons (Firewall / Bastion) enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.hub-networking-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nhnnadd' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2023-07-01' = { + name: resourceGroupName + location: resourceLocation +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +var addressPrefix = '10.0.0.0/16' + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + // You parameters go here + location: resourceLocation + hubVirtualNetworks: { + hub1: { + addressPrefixes: array(addressPrefix) + enableAzureFirewall: false + enableBastion: false + enablePeering: false + enableTelemetry: true + flowTimeoutInMinutes: 30 + dnsServers: ['10.0.1.6', '10.0.1.7'] + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + location: resourceLocation + lock: { + kind: 'CanNotDelete' + name: 'hub1Lock' + } + routes: [ + { + name: 'defaultRoute' + properties: { + addressPrefix: '0.0.0.0/0' + nextHopType: 'Internet' + } + } + ] + subnets: [ + { + name: 'GatewaySubnet' + addressPrefix: cidrSubnet(addressPrefix, 26, 0) + } + { + name: 'AzureFirewallSubnet' + addressPrefix: cidrSubnet(addressPrefix, 26, 1) + } + { + name: 'AzureBastionSubnet' + addressPrefix: cidrSubnet(addressPrefix, 26, 2) + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + vnetEncryption: false + vnetEncryptionEnforcement: 'AllowUnencrypted' + } + } + } + } +] From 57769c252159f9c25c12e67bc33938f6790236c8 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Thu, 26 Sep 2024 03:08:22 +0200 Subject: [PATCH 38/57] fix: StorageAccount - Added implicit dependency to blobServices from container (#3254) ## Description - Added implicit dependency to blobServices from container Closes #3210 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.storage.storage-account](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml/badge.svg?branch=users%2Falsehr%2FsaDep&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --- .../blob-service/container/README.md | 9 +++++++ .../blob-service/container/main.bicep | 5 +++- .../blob-service/container/main.json | 19 ++++++++----- .../storage-account/blob-service/main.bicep | 1 + .../storage-account/blob-service/main.json | 25 ++++++++++++----- avm/res/storage/storage-account/main.json | 27 +++++++++++++------ 6 files changed, 64 insertions(+), 22 deletions(-) diff --git a/avm/res/storage/storage-account/blob-service/container/README.md b/avm/res/storage/storage-account/blob-service/container/README.md index c66f943899..34d861e3b5 100644 --- a/avm/res/storage/storage-account/blob-service/container/README.md +++ b/avm/res/storage/storage-account/blob-service/container/README.md @@ -34,6 +34,7 @@ This module deploys a Storage Account Blob Container. | Parameter | Type | Description | | :-- | :-- | :-- | +| [`blobServiceName`](#parameter-blobservicename) | string | The name of the parent Blob Service. Required if the template is used in a standalone deployment. | | [`defaultEncryptionScope`](#parameter-defaultencryptionscope) | string | Default the container to use specified encryption scope for all writes. | | [`denyEncryptionScopeOverride`](#parameter-denyencryptionscopeoverride) | bool | Block override of encryption scope from the container default. | | [`enableNfsV3AllSquash`](#parameter-enablenfsv3allsquash) | bool | Enable NFSv3 all squash on blob container. | @@ -59,6 +60,14 @@ The name of the parent Storage Account. Required if the template is used in a st - Required: Yes - Type: string +### Parameter: `blobServiceName` + +The name of the parent Blob Service. Required if the template is used in a standalone deployment. + +- Required: No +- Type: string +- Default: `'default'` + ### Parameter: `defaultEncryptionScope` Default the container to use specified encryption scope for all writes. diff --git a/avm/res/storage/storage-account/blob-service/container/main.bicep b/avm/res/storage/storage-account/blob-service/container/main.bicep index fa0193da72..9a19a6096f 100644 --- a/avm/res/storage/storage-account/blob-service/container/main.bicep +++ b/avm/res/storage/storage-account/blob-service/container/main.bicep @@ -6,6 +6,9 @@ metadata owner = 'Azure/module-maintainers' @description('Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment.') param storageAccountName string +@description('Optional. The name of the parent Blob Service. Required if the template is used in a standalone deployment.') +param blobServiceName string = 'default' + @description('Required. The name of the storage container to deploy.') param name string @@ -105,7 +108,7 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' existing name: storageAccountName resource blobServices 'blobServices@2022-09-01' existing = { - name: 'default' + name: blobServiceName } } diff --git a/avm/res/storage/storage-account/blob-service/container/main.json b/avm/res/storage/storage-account/blob-service/container/main.json index 98d00e679f..92a5cbbbd0 100644 --- a/avm/res/storage/storage-account/blob-service/container/main.json +++ b/avm/res/storage/storage-account/blob-service/container/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "1020003258393866601" + "templateHash": "3558916747425087131" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -95,6 +95,13 @@ "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." } }, + "blobServiceName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the parent Blob Service. Required if the template is used in a standalone deployment." + } + }, "name": { "type": "string", "metadata": { @@ -205,7 +212,7 @@ "existing": true, "type": "Microsoft.Storage/storageAccounts/blobServices", "apiVersion": "2022-09-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('blobServiceName'))]", "dependsOn": [ "storageAccount" ] @@ -219,7 +226,7 @@ "container": { "type": "Microsoft.Storage/storageAccounts/blobServices/containers", "apiVersion": "2022-09-01", - "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]", "properties": { "defaultEncryptionScope": "[if(not(empty(parameters('defaultEncryptionScope'))), parameters('defaultEncryptionScope'), null())]", "denyEncryptionScopeOverride": "[if(equals(parameters('denyEncryptionScopeOverride'), true()), parameters('denyEncryptionScopeOverride'), null())]", @@ -240,8 +247,8 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}/containers/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}/containers/{2}', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", @@ -387,7 +394,7 @@ "metadata": { "description": "The resource ID of the deployed container." }, - "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name'))]" + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", diff --git a/avm/res/storage/storage-account/blob-service/main.bicep b/avm/res/storage/storage-account/blob-service/main.bicep index c02c02f6fa..bb43c4d610 100644 --- a/avm/res/storage/storage-account/blob-service/main.bicep +++ b/avm/res/storage/storage-account/blob-service/main.bicep @@ -149,6 +149,7 @@ module blobServices_container 'container/main.bicep' = [ name: '${deployment().name}-Container-${index}' params: { storageAccountName: storageAccount.name + blobServiceName: blobServices.name name: container.name defaultEncryptionScope: container.?defaultEncryptionScope denyEncryptionScopeOverride: container.?denyEncryptionScopeOverride diff --git a/avm/res/storage/storage-account/blob-service/main.json b/avm/res/storage/storage-account/blob-service/main.json index 7531267468..944846b041 100644 --- a/avm/res/storage/storage-account/blob-service/main.json +++ b/avm/res/storage/storage-account/blob-service/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "17077763197163073998" + "templateHash": "16657059190807174649" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service.", @@ -365,6 +365,9 @@ "storageAccountName": { "value": "[parameters('storageAccountName')]" }, + "blobServiceName": { + "value": "[variables('name')]" + }, "name": { "value": "[coalesce(parameters('containers'), createArray())[copyIndex()].name]" }, @@ -404,7 +407,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "1020003258393866601" + "templateHash": "3558916747425087131" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -493,6 +496,13 @@ "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." } }, + "blobServiceName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the parent Blob Service. Required if the template is used in a standalone deployment." + } + }, "name": { "type": "string", "metadata": { @@ -603,7 +613,7 @@ "existing": true, "type": "Microsoft.Storage/storageAccounts/blobServices", "apiVersion": "2022-09-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('blobServiceName'))]", "dependsOn": [ "storageAccount" ] @@ -617,7 +627,7 @@ "container": { "type": "Microsoft.Storage/storageAccounts/blobServices/containers", "apiVersion": "2022-09-01", - "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]", "properties": { "defaultEncryptionScope": "[if(not(empty(parameters('defaultEncryptionScope'))), parameters('defaultEncryptionScope'), null())]", "denyEncryptionScopeOverride": "[if(equals(parameters('denyEncryptionScopeOverride'), true()), parameters('denyEncryptionScopeOverride'), null())]", @@ -638,8 +648,8 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}/containers/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}/containers/{2}', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", @@ -785,7 +795,7 @@ "metadata": { "description": "The resource ID of the deployed container." }, - "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name'))]" + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", @@ -798,6 +808,7 @@ } }, "dependsOn": [ + "blobServices", "storageAccount" ] } diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index 38e3f3d998..8120b7936e 100644 --- a/avm/res/storage/storage-account/main.json +++ b/avm/res/storage/storage-account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "6735651687082765200" + "templateHash": "8986504733456130232" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account.", @@ -2266,7 +2266,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "17077763197163073998" + "templateHash": "16657059190807174649" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service.", @@ -2625,6 +2625,9 @@ "storageAccountName": { "value": "[parameters('storageAccountName')]" }, + "blobServiceName": { + "value": "[variables('name')]" + }, "name": { "value": "[coalesce(parameters('containers'), createArray())[copyIndex()].name]" }, @@ -2664,7 +2667,7 @@ "_generator": { "name": "bicep", "version": "0.29.47.4906", - "templateHash": "1020003258393866601" + "templateHash": "3558916747425087131" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -2753,6 +2756,13 @@ "description": "Conditional. The name of the parent Storage Account. Required if the template is used in a standalone deployment." } }, + "blobServiceName": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the parent Blob Service. Required if the template is used in a standalone deployment." + } + }, "name": { "type": "string", "metadata": { @@ -2863,7 +2873,7 @@ "existing": true, "type": "Microsoft.Storage/storageAccounts/blobServices", "apiVersion": "2022-09-01", - "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]", + "name": "[format('{0}/{1}', parameters('storageAccountName'), parameters('blobServiceName'))]", "dependsOn": [ "storageAccount" ] @@ -2877,7 +2887,7 @@ "container": { "type": "Microsoft.Storage/storageAccounts/blobServices/containers", "apiVersion": "2022-09-01", - "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", + "name": "[format('{0}/{1}/{2}', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]", "properties": { "defaultEncryptionScope": "[if(not(empty(parameters('defaultEncryptionScope'))), parameters('defaultEncryptionScope'), null())]", "denyEncryptionScopeOverride": "[if(equals(parameters('denyEncryptionScopeOverride'), true()), parameters('denyEncryptionScopeOverride'), null())]", @@ -2898,8 +2908,8 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}/containers/{2}', parameters('storageAccountName'), 'default', parameters('name'))]", - "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}/containers/{2}', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", @@ -3045,7 +3055,7 @@ "metadata": { "description": "The resource ID of the deployed container." }, - "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), 'default', parameters('name'))]" + "value": "[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', parameters('storageAccountName'), parameters('blobServiceName'), parameters('name'))]" }, "resourceGroupName": { "type": "string", @@ -3058,6 +3068,7 @@ } }, "dependsOn": [ + "blobServices", "storageAccount" ] } From 717e7fd82c698a28e2d152c2b1c39ddee6fe48ac Mon Sep 17 00:00:00 2001 From: Buddy <38195643+tsc-buddy@users.noreply.github.com> Date: Thu, 26 Sep 2024 21:00:53 +1200 Subject: [PATCH 39/57] fix: test updates for az defaults (#3344) ## Description Addressing the AZ defaults now that the PSRule test bug has been addressed [here](https://github.com/microsoft/PSRule/issues/1900#issuecomment-2356575578). ## Pipeline Reference | Pipeline | | -------- | |[![avm.res.web.serverfarm](https://github.com/tsc-buddy/bicep-registry-modules/actions/workflows/avm.res.web.serverfarm.yml/badge.svg?branch=fix%2Fasp-zr-default)](https://github.com/tsc-buddy/bicep-registry-modules/actions/workflows/avm.res.web.serverfarm.yml)| ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [x] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- avm/res/web/serverfarm/README.md | 96 +++++++++---------- avm/res/web/serverfarm/main.bicep | 19 ++-- avm/res/web/serverfarm/main.json | 12 ++- .../tests/e2e/defaults/main.test.bicep | 4 +- .../serverfarm/tests/e2e/max/main.test.bicep | 8 +- .../tests/e2e/waf-aligned/main.test.bicep | 6 +- 6 files changed, 70 insertions(+), 75 deletions(-) diff --git a/avm/res/web/serverfarm/README.md b/avm/res/web/serverfarm/README.md index 0df60a5510..152351ffe2 100644 --- a/avm/res/web/serverfarm/README.md +++ b/avm/res/web/serverfarm/README.md @@ -46,8 +46,6 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { params: { // Required parameters name: 'wsfmin001' - skuCapacity: 2 - skuName: 'S1' // Non-required parameters location: '' } @@ -70,12 +68,6 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { "name": { "value": "wsfmin001" }, - "skuCapacity": { - "value": 2 - }, - "skuName": { - "value": "S1" - }, // Non-required parameters "location": { "value": "" @@ -102,8 +94,6 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { params: { // Required parameters name: 'wsfmax001' - skuCapacity: 1 - skuName: 'S1' // Non-required parameters diagnosticSettings: [ { @@ -143,12 +133,14 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { roleDefinitionIdOrName: '' } ] + skuCapacity: 3 + skuName: 'P1v3' tags: { Environment: 'Non-Prod' 'hidden-title': 'This is visible in the resource name' Role: 'DeploymentValidation' } - zoneRedundant: false + zoneRedundant: true } } ``` @@ -169,12 +161,6 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { "name": { "value": "wsfmax001" }, - "skuCapacity": { - "value": 1 - }, - "skuName": { - "value": "S1" - }, // Non-required parameters "diagnosticSettings": { "value": [ @@ -226,6 +212,12 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { } ] }, + "skuCapacity": { + "value": 3 + }, + "skuName": { + "value": "P1v3" + }, "tags": { "value": { "Environment": "Non-Prod", @@ -234,7 +226,7 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { } }, "zoneRedundant": { - "value": false + "value": true } } } @@ -258,8 +250,6 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { params: { // Required parameters name: 'wsfwaf001' - skuCapacity: 2 - skuName: 'P1v3' // Non-required parameters diagnosticSettings: [ { @@ -281,12 +271,14 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { kind: 'CanNotDelete' name: 'lock' } + skuCapacity: 3 + skuName: 'P1v3' tags: { Environment: 'Non-Prod' 'hidden-title': 'This is visible in the resource name' Role: 'DeploymentValidation' } - zoneRedundant: false + zoneRedundant: true } } ``` @@ -307,12 +299,6 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { "name": { "value": "wsfwaf001" }, - "skuCapacity": { - "value": 2 - }, - "skuName": { - "value": "P1v3" - }, // Non-required parameters "diagnosticSettings": { "value": [ @@ -342,6 +328,12 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { "name": "lock" } }, + "skuCapacity": { + "value": 3 + }, + "skuName": { + "value": "P1v3" + }, "tags": { "value": { "Environment": "Non-Prod", @@ -350,7 +342,7 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { } }, "zoneRedundant": { - "value": false + "value": true } } } @@ -366,8 +358,6 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { | Parameter | Type | Description | | :-- | :-- | :-- | | [`name`](#parameter-name) | string | Name of the app service plan. | -| [`skuCapacity`](#parameter-skucapacity) | int | Number of workers associated with the App Service Plan. | -| [`skuName`](#parameter-skuname) | string | The name of the SKU will Determine the tier, size, family of the App Service Plan. | **Conditional parameters** @@ -389,6 +379,8 @@ module serverfarm 'br/public:avm/res/web/serverfarm:' = { | [`maximumElasticWorkerCount`](#parameter-maximumelasticworkercount) | int | Maximum number of total workers allowed for this ElasticScaleEnabled App Service Plan. | | [`perSiteScaling`](#parameter-persitescaling) | bool | If true, apps assigned to this App Service plan can be scaled independently. If false, apps assigned to this App Service plan will scale to all instances of the plan. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`skuCapacity`](#parameter-skucapacity) | int | Number of workers associated with the App Service Plan. This defaults to 3, to leverage availability zones. | +| [`skuName`](#parameter-skuname) | string | The name of the SKU will Determine the tier, size, family of the App Service Plan. This defaults to P1v3 to leverage availability zones. | | [`tags`](#parameter-tags) | object | Tags of the resource. | | [`targetWorkerCount`](#parameter-targetworkercount) | int | Scaling worker count. | | [`targetWorkerSize`](#parameter-targetworkersize) | int | The instance size of the hosting plan (small, medium, or large). | @@ -402,27 +394,6 @@ Name of the app service plan. - Required: Yes - Type: string -### Parameter: `skuCapacity` - -Number of workers associated with the App Service Plan. - -- Required: Yes -- Type: int - -### Parameter: `skuName` - -The name of the SKU will Determine the tier, size, family of the App Service Plan. - -- Required: Yes -- Type: string -- Example: - ```Bicep - 'F1' - 'B1' - 'P1v3' - 'I1v2' - ``` - ### Parameter: `reserved` Defaults to false when creating Windows/app App Service Plan. Required if creating a Linux App Service Plan and must be set to true. @@ -739,6 +710,29 @@ The principal type of the assigned principal ID. ] ``` +### Parameter: `skuCapacity` + +Number of workers associated with the App Service Plan. This defaults to 3, to leverage availability zones. + +- Required: No +- Type: int +- Default: `3` + +### Parameter: `skuName` + +The name of the SKU will Determine the tier, size, family of the App Service Plan. This defaults to P1v3 to leverage availability zones. + +- Required: No +- Type: string +- Default: `'P1v3'` +- Example: + ```Bicep + 'F1' + 'B1' + 'P1v3' + 'I1v2' + ``` + ### Parameter: `tags` Tags of the resource. diff --git a/avm/res/web/serverfarm/main.bicep b/avm/res/web/serverfarm/main.bicep index cd823f28ce..a9dc015538 100644 --- a/avm/res/web/serverfarm/main.bicep +++ b/avm/res/web/serverfarm/main.bicep @@ -7,7 +7,7 @@ metadata owner = 'Azure/module-maintainers' @maxLength(60) param name string -@description('Required. The name of the SKU will Determine the tier, size, family of the App Service Plan.') +@description('Optional. The name of the SKU will Determine the tier, size, family of the App Service Plan. This defaults to P1v3 to leverage availability zones.') @metadata({ example: ''' 'F1' @@ -16,10 +16,10 @@ param name string 'I1v2' ''' }) -param skuName string +param skuName string = 'P1v3' -@description('Required. Number of workers associated with the App Service Plan.') -param skuCapacity int +@description('Optional. Number of workers associated with the App Service Plan. This defaults to 3, to leverage availability zones.') +param skuCapacity int = 3 @description('Optional. Location for all resources.') param location string = resourceGroup().location @@ -185,11 +185,12 @@ resource appServicePlan_roleAssignments 'Microsoft.Authorization/roleAssignments for (roleAssignment, index) in (roleAssignments ?? []): { name: guid(appServicePlan.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName) properties: { - roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName) - ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName] - : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/') - ? roleAssignment.roleDefinitionIdOrName - : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName) + roleDefinitionId: builtInRoleNames[?roleAssignment.roleDefinitionIdOrName] ?? (contains( + roleAssignment.roleDefinitionIdOrName, + '/providers/Microsoft.Authorization/roleDefinitions/' + ) + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName)) principalId: roleAssignment.principalId description: roleAssignment.?description principalType: roleAssignment.?principalType diff --git a/avm/res/web/serverfarm/main.json b/avm/res/web/serverfarm/main.json index 68729b8033..ba4108ee0b 100644 --- a/avm/res/web/serverfarm/main.json +++ b/avm/res/web/serverfarm/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16609348340052214807" + "version": "0.30.3.12046", + "templateHash": "12599229174633311842" }, "name": "App Service Plan", "description": "This module deploys an App Service Plan.", @@ -203,15 +203,17 @@ }, "skuName": { "type": "string", + "defaultValue": "P1v3", "metadata": { "example": " 'F1'\n 'B1'\n 'P1v3'\n 'I1v2'\n ", - "description": "Required. The name of the SKU will Determine the tier, size, family of the App Service Plan." + "description": "Optional. The name of the SKU will Determine the tier, size, family of the App Service Plan. This defaults to P1v3 to leverage availability zones." } }, "skuCapacity": { "type": "int", + "defaultValue": 3, "metadata": { - "description": "Required. Number of workers associated with the App Service Plan." + "description": "Optional. Number of workers associated with the App Service Plan. This defaults to 3, to leverage availability zones." } }, "location": { @@ -447,7 +449,7 @@ "scope": "[format('Microsoft.Web/serverfarms/{0}', parameters('name'))]", "name": "[guid(resourceId('Microsoft.Web/serverfarms', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", + "roleDefinitionId": "[coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]", "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", diff --git a/avm/res/web/serverfarm/tests/e2e/defaults/main.test.bicep b/avm/res/web/serverfarm/tests/e2e/defaults/main.test.bicep index 1de03468ef..afb9a3ac26 100644 --- a/avm/res/web/serverfarm/tests/e2e/defaults/main.test.bicep +++ b/avm/res/web/serverfarm/tests/e2e/defaults/main.test.bicep @@ -18,7 +18,7 @@ param serviceShort string = 'wsfmin' param namePrefix string = '#_namePrefix_#' #disable-next-line no-hardcoded-location // Just a value to avoid ongoing capacity challenges -var enforcedLocation = 'eastus' +var enforcedLocation = 'australiaeast' // ============ // // Dependencies // @@ -43,8 +43,6 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: enforcedLocation - skuName: 'S1' - skuCapacity: 2 } } ] diff --git a/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep b/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep index b5d2046b5f..7b0bd49b7c 100644 --- a/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep +++ b/avm/res/web/serverfarm/tests/e2e/max/main.test.bicep @@ -18,7 +18,7 @@ param serviceShort string = 'wsfmax' param namePrefix string = '#_namePrefix_#' #disable-next-line no-hardcoded-location // Just a value to avoid ongoing capacity challenges -var enforcedLocation = 'eastus' +var enforcedLocation = 'australiaeast' // ============ // // Dependencies // @@ -64,10 +64,10 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' location: enforcedLocation - skuName: 'S1' - skuCapacity: 1 + skuName: 'P1v3' + skuCapacity: 3 perSiteScaling: true - zoneRedundant: false + zoneRedundant: true kind: 'App' lock: { name: 'lock' diff --git a/avm/res/web/serverfarm/tests/e2e/waf-aligned/main.test.bicep b/avm/res/web/serverfarm/tests/e2e/waf-aligned/main.test.bicep index 15eb7e25be..3e0e62dd53 100644 --- a/avm/res/web/serverfarm/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/web/serverfarm/tests/e2e/waf-aligned/main.test.bicep @@ -18,7 +18,7 @@ param serviceShort string = 'wsfwaf' param namePrefix string = '#_namePrefix_#' #disable-next-line no-hardcoded-location // Just a value to avoid ongoing capacity challenges -var enforcedLocation = 'eastus' +var enforcedLocation = 'australiaeast' // ============ // // Dependencies // @@ -56,8 +56,8 @@ module testDeployment '../../../main.bicep' = [ name: '${namePrefix}${serviceShort}001' location: enforcedLocation skuName: 'P1v3' - skuCapacity: 2 - zoneRedundant: false + skuCapacity: 3 + zoneRedundant: true kind: 'App' lock: { name: 'lock' From 032457048e32f131aa9578da896bc0b801ecd0e5 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 26 Sep 2024 11:37:22 +0200 Subject: [PATCH 40/57] fix: Added a fixed uksouth location for `avm.res.dev-ops-infrastructure.pool` (#3351) ## Description This pull request introduces a fixed location (uksouth) used to run the validation deployments. ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.dev-ops-infrastructure.pool](https://github.com/johnlokerse/bicep-registry-modules/actions/workflows/avm.res.dev-ops-infrastructure.pool.yml/badge.svg?branch=johnlokerse%2Fenforce-location-mdp)](https://github.com/johnlokerse/bicep-registry-modules/actions/workflows/avm.res.dev-ops-infrastructure.pool.yml) | ## Type of Change - [x] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/dev-ops-infrastructure/pool/main.json | 4 ++-- .../pool/tests/e2e/defaults/main.test.bicep | 17 +++++++++-------- .../pool/tests/e2e/max/main.test.bicep | 17 +++++++++-------- .../pool/tests/e2e/waf-aligned/main.test.bicep | 17 +++++++++-------- 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/avm/res/dev-ops-infrastructure/pool/main.json b/avm/res/dev-ops-infrastructure/pool/main.json index ce80f4d180..689ef6eb8c 100644 --- a/avm/res/dev-ops-infrastructure/pool/main.json +++ b/avm/res/dev-ops-infrastructure/pool/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.3.12046", - "templateHash": "15191897376801297199" + "version": "0.30.23.60470", + "templateHash": "3502193398932835678" }, "name": "Managed DevOps Pool", "description": "This module deploys the Managed DevOps Pool resource.", diff --git a/avm/res/dev-ops-infrastructure/pool/tests/e2e/defaults/main.test.bicep b/avm/res/dev-ops-infrastructure/pool/tests/e2e/defaults/main.test.bicep index c21a95f505..45332f3a95 100644 --- a/avm/res/dev-ops-infrastructure/pool/tests/e2e/defaults/main.test.bicep +++ b/avm/res/dev-ops-infrastructure/pool/tests/e2e/defaults/main.test.bicep @@ -10,25 +10,26 @@ metadata description = 'This instance deploys the module with the minimum set of @maxLength(90) param resourceGroupName string = 'dep-${namePrefix}-dev-ops-infrastructure.pool-${serviceShort}-rg' -@description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location - @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') param serviceShort string = 'mdpmin' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' -@description('Required. Name of the Azure DevOps Organization. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-AzureDevOpsOrganizationName\'.') +@description('Required. Name of the Azure DevOps organization. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-AzureDevOpsOrganizationName\'.') @secure() param azureDevOpsOrganizationName string = '' +// The Managed DevOps Pools resource is not available in all regions +#disable-next-line no-hardcoded-location +var enforcedLocation = 'uksouth' + // ============ // // Dependencies // // ============ // module nestedDependencies 'dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' params: { devCenterName: 'dep-${namePrefix}-dc-${serviceShort}' devCenterProjectName: 'dep-${namePrefix}-dcp-${serviceShort}' @@ -40,7 +41,7 @@ module nestedDependencies 'dependencies.bicep' = { // ================= // resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: resourceGroupName - location: resourceLocation + location: enforcedLocation } // ============== // @@ -50,10 +51,10 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { module testDeployment '../../../main.bicep' = [ for iteration in ['init', 'idem']: { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}001' - location: resourceLocation + location: enforcedLocation agentProfile: { kind: 'Stateless' } diff --git a/avm/res/dev-ops-infrastructure/pool/tests/e2e/max/main.test.bicep b/avm/res/dev-ops-infrastructure/pool/tests/e2e/max/main.test.bicep index 7416f2934c..09b40200e5 100644 --- a/avm/res/dev-ops-infrastructure/pool/tests/e2e/max/main.test.bicep +++ b/avm/res/dev-ops-infrastructure/pool/tests/e2e/max/main.test.bicep @@ -11,16 +11,13 @@ metadata description = 'This instance deploys the module with most of its featur @maxLength(90) param resourceGroupName string = 'dep-${namePrefix}-dev-ops-infrastructure.pool-${serviceShort}-rg' -@description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location - @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') param serviceShort string = 'mdpmax' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' -@description('Required. Name of the Azure DevOps Organization. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-AzureDevOpsOrganizationName\'.') +@description('Required. Name of the Azure DevOps organization. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-AzureDevOpsOrganizationName\'.') @secure() param azureDevOpsOrganizationName string = '' @@ -32,12 +29,16 @@ param azureDevOpsProjectName string = '' @secure() param devOpsInfrastructureObjectID string = '' +// The Managed DevOps Pools resource is not available in all regions +#disable-next-line no-hardcoded-location +var enforcedLocation = 'uksouth' + // ============ // // Dependencies // // ============ // module nestedDependencies 'dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' params: { devCenterName: 'dep-${namePrefix}-dc-${serviceShort}' devCenterProjectName: 'dep-${namePrefix}-dcp-${serviceShort}' @@ -51,7 +52,7 @@ module nestedDependencies 'dependencies.bicep' = { // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: resourceGroupName - location: resourceLocation + location: enforcedLocation } // ============== // @@ -62,10 +63,10 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { module testDeployment '../../../main.bicep' = [ for iteration in ['init', 'idem']: { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}001' - location: resourceLocation + location: enforcedLocation agentProfile: { kind: 'Stateless' resourcePredictions: { diff --git a/avm/res/dev-ops-infrastructure/pool/tests/e2e/waf-aligned/main.test.bicep b/avm/res/dev-ops-infrastructure/pool/tests/e2e/waf-aligned/main.test.bicep index fdf015444f..20a04a3900 100644 --- a/avm/res/dev-ops-infrastructure/pool/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/dev-ops-infrastructure/pool/tests/e2e/waf-aligned/main.test.bicep @@ -11,16 +11,13 @@ metadata description = 'This instance deploys the module in alignment with the b @maxLength(90) param resourceGroupName string = 'dep-${namePrefix}-dev-ops-infrastructure.pool-${serviceShort}-rg' -@description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location - @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') param serviceShort string = 'mdpwaf' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' -@description('Required. Name of the Azure DevOps Organization. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-AzureDevOpsOrganizationName\'.') +@description('Required. Name of the Azure DevOps organization. This value is tenant-specific and must be stored in the CI Key Vault in a secret named \'CI-AzureDevOpsOrganizationName\'.') @secure() param azureDevOpsOrganizationName string = '' @@ -32,12 +29,16 @@ param azureDevOpsProjectName string = '' @secure() param devOpsInfrastructureObjectID string = '' +// The Managed DevOps Pools resource is not available in all regions +#disable-next-line no-hardcoded-location +var enforcedLocation = 'uksouth' + // ============ // // Dependencies // // ============ // module nestedDependencies 'dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + name: '${uniqueString(deployment().name, enforcedLocation)}-nestedDependencies' params: { devCenterName: 'dep-${namePrefix}-dc-${serviceShort}' devCenterProjectName: 'dep-${namePrefix}-dcp-${serviceShort}' @@ -51,7 +52,7 @@ module nestedDependencies 'dependencies.bicep' = { // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: resourceGroupName - location: resourceLocation + location: enforcedLocation } // ============== // @@ -62,10 +63,10 @@ resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { module testDeployment '../../../main.bicep' = [ for iteration in ['init', 'idem']: { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + name: '${uniqueString(deployment().name, enforcedLocation)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}001' - location: resourceLocation + location: enforcedLocation agentProfile: { kind: 'Stateless' resourcePredictions: { From 0c002037f359c4e40ecf878f946ccf7ccd4916c9 Mon Sep 17 00:00:00 2001 From: Peter Budai Date: Fri, 27 Sep 2024 03:09:13 +0200 Subject: [PATCH 41/57] fix: Rename 'capacity' property to skuCapacity in the tests - `avm/res/sql/server` (#3318) Fixes #1324 ## Description ## Pipeline Reference | Pipeline | | -------- | | | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- avm/res/sql/server/README.md | 8 ++++---- avm/res/sql/server/tests/e2e/max/main.test.bicep | 2 +- avm/res/sql/server/tests/e2e/waf-aligned/main.test.bicep | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/avm/res/sql/server/README.md b/avm/res/sql/server/README.md index 4f73df2fa8..dcc3f4e02f 100644 --- a/avm/res/sql/server/README.md +++ b/avm/res/sql/server/README.md @@ -270,7 +270,6 @@ module server 'br/public:avm/res/sql/server:' = { backupShortTermRetentionPolicy: { retentionDays: 14 } - capacity: 0 collation: 'SQL_Latin1_General_CP1_CI_AS' diagnosticSettings: [ { @@ -289,6 +288,7 @@ module server 'br/public:avm/res/sql/server:' = { licenseType: 'LicenseIncluded' maxSizeBytes: 34359738368 name: 'sqlsmaxdb-001' + skuCapacity: 0 skuName: 'ElasticPool' skuTier: 'GeneralPurpose' } @@ -438,7 +438,6 @@ module server 'br/public:avm/res/sql/server:' = { "backupShortTermRetentionPolicy": { "retentionDays": 14 }, - "capacity": 0, "collation": "SQL_Latin1_General_CP1_CI_AS", "diagnosticSettings": [ { @@ -457,6 +456,7 @@ module server 'br/public:avm/res/sql/server:' = { "licenseType": "LicenseIncluded", "maxSizeBytes": 34359738368, "name": "sqlsmaxdb-001", + "skuCapacity": 0, "skuName": "ElasticPool", "skuTier": "GeneralPurpose" } @@ -857,7 +857,6 @@ module server 'br/public:avm/res/sql/server:' = { backupShortTermRetentionPolicy: { retentionDays: 14 } - capacity: 0 collation: 'SQL_Latin1_General_CP1_CI_AS' diagnosticSettings: [ { @@ -876,6 +875,7 @@ module server 'br/public:avm/res/sql/server:' = { licenseType: 'LicenseIncluded' maxSizeBytes: 34359738368 name: 'sqlswafdb-001' + skuCapacity: 0 skuName: 'ElasticPool' skuTier: 'GeneralPurpose' } @@ -988,7 +988,6 @@ module server 'br/public:avm/res/sql/server:' = { "backupShortTermRetentionPolicy": { "retentionDays": 14 }, - "capacity": 0, "collation": "SQL_Latin1_General_CP1_CI_AS", "diagnosticSettings": [ { @@ -1007,6 +1006,7 @@ module server 'br/public:avm/res/sql/server:' = { "licenseType": "LicenseIncluded", "maxSizeBytes": 34359738368, "name": "sqlswafdb-001", + "skuCapacity": 0, "skuName": "ElasticPool", "skuTier": "GeneralPurpose" } diff --git a/avm/res/sql/server/tests/e2e/max/main.test.bicep b/avm/res/sql/server/tests/e2e/max/main.test.bicep index 662e8900fd..ec70abad1a 100644 --- a/avm/res/sql/server/tests/e2e/max/main.test.bicep +++ b/avm/res/sql/server/tests/e2e/max/main.test.bicep @@ -125,7 +125,7 @@ module testDeployment '../../../main.bicep' = { collation: 'SQL_Latin1_General_CP1_CI_AS' skuTier: 'GeneralPurpose' skuName: 'ElasticPool' - capacity: 0 + skuCapacity: 0 maxSizeBytes: 34359738368 licenseType: 'LicenseIncluded' diagnosticSettings: [ diff --git a/avm/res/sql/server/tests/e2e/waf-aligned/main.test.bicep b/avm/res/sql/server/tests/e2e/waf-aligned/main.test.bicep index f444f7bf12..2f7e279f3b 100644 --- a/avm/res/sql/server/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/sql/server/tests/e2e/waf-aligned/main.test.bicep @@ -100,7 +100,7 @@ module testDeployment '../../../main.bicep' = { collation: 'SQL_Latin1_General_CP1_CI_AS' skuTier: 'GeneralPurpose' skuName: 'ElasticPool' - capacity: 0 + skuCapacity: 0 maxSizeBytes: 34359738368 licenseType: 'LicenseIncluded' diagnosticSettings: [ From 3c32902929de734daeeee6cc90070061922a2d85 Mon Sep 17 00:00:00 2001 From: Erika Gressi <56914614+eriqua@users.noreply.github.com> Date: Fri, 27 Sep 2024 08:34:10 +0100 Subject: [PATCH 42/57] fix: Storage account readme regen (#3372) ## Description Fixing static validation pipeline run Recompiled json files to trigger module publishing, blocked for latest merge ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.storage.storage-account](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml/badge.svg?branch=users%2Feriqua%2Ffix-storage)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/storage/storage-account/README.md | 12 ++-- .../container/immutability-policy/main.json | 4 +- .../blob-service/container/main.json | 8 +-- .../storage-account/blob-service/main.json | 12 ++-- .../storage-account/file-service/README.md | 2 +- .../storage-account/file-service/main.json | 12 ++-- .../file-service/share/main.json | 8 +-- .../storage-account/local-user/README.md | 2 +- .../storage-account/local-user/main.json | 4 +- avm/res/storage/storage-account/main.json | 56 +++++++++---------- .../management-policy/main.json | 4 +- .../storage-account/queue-service/README.md | 4 +- .../storage-account/queue-service/main.json | 8 +-- .../queue-service/queue/README.md | 2 +- .../queue-service/queue/main.json | 4 +- .../storage-account/table-service/README.md | 4 +- .../storage-account/table-service/main.json | 8 +-- .../table-service/table/README.md | 2 +- .../table-service/table/main.json | 4 +- 19 files changed, 80 insertions(+), 80 deletions(-) diff --git a/avm/res/storage/storage-account/README.md b/avm/res/storage/storage-account/README.md index bafe70194f..310335b0d2 100644 --- a/avm/res/storage/storage-account/README.md +++ b/avm/res/storage/storage-account/README.md @@ -26,14 +26,14 @@ This module deploys a Storage Account. | `Microsoft.Storage/storageAccounts/blobServices` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices) | | `Microsoft.Storage/storageAccounts/blobServices/containers` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers) | | `Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers/immutabilityPolicies) | -| `Microsoft.Storage/storageAccounts/fileServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/fileServices) | +| `Microsoft.Storage/storageAccounts/fileServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/fileServices) | | `Microsoft.Storage/storageAccounts/fileServices/shares` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-01-01/storageAccounts/fileServices/shares) | -| `Microsoft.Storage/storageAccounts/localUsers` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/localUsers) | +| `Microsoft.Storage/storageAccounts/localUsers` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/localUsers) | | `Microsoft.Storage/storageAccounts/managementPolicies` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-01-01/storageAccounts/managementPolicies) | -| `Microsoft.Storage/storageAccounts/queueServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/queueServices) | -| `Microsoft.Storage/storageAccounts/queueServices/queues` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/queueServices/queues) | -| `Microsoft.Storage/storageAccounts/tableServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/tableServices) | -| `Microsoft.Storage/storageAccounts/tableServices/tables` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/tableServices/tables) | +| `Microsoft.Storage/storageAccounts/queueServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/queueServices) | +| `Microsoft.Storage/storageAccounts/queueServices/queues` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/queueServices/queues) | +| `Microsoft.Storage/storageAccounts/tableServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/tableServices) | +| `Microsoft.Storage/storageAccounts/tableServices/tables` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/tableServices/tables) | ## Usage examples diff --git a/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json b/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json index e92ebe5e3f..1a92a67f27 100644 --- a/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json +++ b/avm/res/storage/storage-account/blob-service/container/immutability-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "7245741358008626948" + "version": "0.30.23.60470", + "templateHash": "17642721918788484059" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy.", diff --git a/avm/res/storage/storage-account/blob-service/container/main.json b/avm/res/storage/storage-account/blob-service/container/main.json index 92a5cbbbd0..1144d31c91 100644 --- a/avm/res/storage/storage-account/blob-service/container/main.json +++ b/avm/res/storage/storage-account/blob-service/container/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3558916747425087131" + "version": "0.30.23.60470", + "templateHash": "7740343838101895320" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -295,8 +295,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "7245741358008626948" + "version": "0.30.23.60470", + "templateHash": "17642721918788484059" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy.", diff --git a/avm/res/storage/storage-account/blob-service/main.json b/avm/res/storage/storage-account/blob-service/main.json index 944846b041..6ab964fa85 100644 --- a/avm/res/storage/storage-account/blob-service/main.json +++ b/avm/res/storage/storage-account/blob-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "16657059190807174649" + "version": "0.30.23.60470", + "templateHash": "12887537147730330940" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service.", @@ -406,8 +406,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3558916747425087131" + "version": "0.30.23.60470", + "templateHash": "7740343838101895320" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -696,8 +696,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "7245741358008626948" + "version": "0.30.23.60470", + "templateHash": "17642721918788484059" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy.", diff --git a/avm/res/storage/storage-account/file-service/README.md b/avm/res/storage/storage-account/file-service/README.md index 3e9748cc67..af032effaa 100644 --- a/avm/res/storage/storage-account/file-service/README.md +++ b/avm/res/storage/storage-account/file-service/README.md @@ -13,7 +13,7 @@ This module deploys a Storage Account File Share Service. | 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) | -| `Microsoft.Storage/storageAccounts/fileServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/fileServices) | +| `Microsoft.Storage/storageAccounts/fileServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/fileServices) | | `Microsoft.Storage/storageAccounts/fileServices/shares` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-01-01/storageAccounts/fileServices/shares) | ## Parameters diff --git a/avm/res/storage/storage-account/file-service/main.json b/avm/res/storage/storage-account/file-service/main.json index 9375230d2f..3e4af3b534 100644 --- a/avm/res/storage/storage-account/file-service/main.json +++ b/avm/res/storage/storage-account/file-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1933197013743223154" + "version": "0.30.23.60470", + "templateHash": "3657184950062156101" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service.", @@ -286,8 +286,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "13477688809575027800" + "version": "0.30.23.60470", + "templateHash": "5694394509785243538" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share.", @@ -493,8 +493,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "10820882302387746924" + "version": "0.30.23.60470", + "templateHash": "11498628270290452072" } }, "parameters": { diff --git a/avm/res/storage/storage-account/file-service/share/main.json b/avm/res/storage/storage-account/file-service/share/main.json index 90dc220560..6f8f81b2dc 100644 --- a/avm/res/storage/storage-account/file-service/share/main.json +++ b/avm/res/storage/storage-account/file-service/share/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "13477688809575027800" + "version": "0.30.23.60470", + "templateHash": "5694394509785243538" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share.", @@ -212,8 +212,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "10820882302387746924" + "version": "0.30.23.60470", + "templateHash": "11498628270290452072" } }, "parameters": { diff --git a/avm/res/storage/storage-account/local-user/README.md b/avm/res/storage/storage-account/local-user/README.md index f8476f2e7a..b311040c23 100644 --- a/avm/res/storage/storage-account/local-user/README.md +++ b/avm/res/storage/storage-account/local-user/README.md @@ -12,7 +12,7 @@ This module deploys a Storage Account Local User, which is used for SFTP authent | Resource Type | API Version | | :-- | :-- | -| `Microsoft.Storage/storageAccounts/localUsers` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/localUsers) | +| `Microsoft.Storage/storageAccounts/localUsers` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/localUsers) | ## Parameters diff --git a/avm/res/storage/storage-account/local-user/main.json b/avm/res/storage/storage-account/local-user/main.json index 3514e02614..8a19e11da9 100644 --- a/avm/res/storage/storage-account/local-user/main.json +++ b/avm/res/storage/storage-account/local-user/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "18130658711251621530" + "version": "0.30.23.60470", + "templateHash": "14184905621772237225" }, "name": "Storage Account Local Users", "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication.", diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index 8120b7936e..90cce4364c 100644 --- a/avm/res/storage/storage-account/main.json +++ b/avm/res/storage/storage-account/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "8986504733456130232" + "version": "0.30.23.60470", + "templateHash": "6607893452071234065" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account.", @@ -1937,8 +1937,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11289787713365096902" + "version": "0.30.23.60470", + "templateHash": "16749766572958481061" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy.", @@ -2047,8 +2047,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "18130658711251621530" + "version": "0.30.23.60470", + "templateHash": "14184905621772237225" }, "name": "Storage Account Local Users", "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication.", @@ -2265,8 +2265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "16657059190807174649" + "version": "0.30.23.60470", + "templateHash": "12887537147730330940" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service.", @@ -2666,8 +2666,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3558916747425087131" + "version": "0.30.23.60470", + "templateHash": "7740343838101895320" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container.", @@ -2956,8 +2956,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "7245741358008626948" + "version": "0.30.23.60470", + "templateHash": "17642721918788484059" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy.", @@ -3136,8 +3136,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1933197013743223154" + "version": "0.30.23.60470", + "templateHash": "3657184950062156101" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service.", @@ -3417,8 +3417,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "13477688809575027800" + "version": "0.30.23.60470", + "templateHash": "5694394509785243538" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share.", @@ -3624,8 +3624,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "10820882302387746924" + "version": "0.30.23.60470", + "templateHash": "11498628270290452072" } }, "parameters": { @@ -3900,8 +3900,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "9552908737955027812" + "version": "0.30.23.60470", + "templateHash": "6947504466788447852" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service.", @@ -4145,8 +4145,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1992900679572007532" + "version": "0.30.23.60470", + "templateHash": "6090221832347220924" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue.", @@ -4416,8 +4416,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15143318143658591417" + "version": "0.30.23.60470", + "templateHash": "6657632516379685259" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service.", @@ -4658,8 +4658,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "16017327978473583176" + "version": "0.30.23.60470", + "templateHash": "7397003163362434404" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table.", @@ -4916,8 +4916,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "986606208324987345" + "version": "0.30.23.60470", + "templateHash": "12263717469683062316" } }, "definitions": { diff --git a/avm/res/storage/storage-account/management-policy/main.json b/avm/res/storage/storage-account/management-policy/main.json index c348d69daf..6acd0abce6 100644 --- a/avm/res/storage/storage-account/management-policy/main.json +++ b/avm/res/storage/storage-account/management-policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11289787713365096902" + "version": "0.30.23.60470", + "templateHash": "16749766572958481061" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy.", diff --git a/avm/res/storage/storage-account/queue-service/README.md b/avm/res/storage/storage-account/queue-service/README.md index ce773dbe3d..94ce2dacff 100644 --- a/avm/res/storage/storage-account/queue-service/README.md +++ b/avm/res/storage/storage-account/queue-service/README.md @@ -14,8 +14,8 @@ This module deploys a Storage Account Queue Service. | :-- | :-- | | `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.Storage/storageAccounts/queueServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/queueServices) | -| `Microsoft.Storage/storageAccounts/queueServices/queues` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/queueServices/queues) | +| `Microsoft.Storage/storageAccounts/queueServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/queueServices) | +| `Microsoft.Storage/storageAccounts/queueServices/queues` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/queueServices/queues) | ## Parameters diff --git a/avm/res/storage/storage-account/queue-service/main.json b/avm/res/storage/storage-account/queue-service/main.json index 00065e2abe..6fc76d0b47 100644 --- a/avm/res/storage/storage-account/queue-service/main.json +++ b/avm/res/storage/storage-account/queue-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "9552908737955027812" + "version": "0.30.23.60470", + "templateHash": "6947504466788447852" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service.", @@ -250,8 +250,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1992900679572007532" + "version": "0.30.23.60470", + "templateHash": "6090221832347220924" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue.", diff --git a/avm/res/storage/storage-account/queue-service/queue/README.md b/avm/res/storage/storage-account/queue-service/queue/README.md index ccfbd4b635..140ca97388 100644 --- a/avm/res/storage/storage-account/queue-service/queue/README.md +++ b/avm/res/storage/storage-account/queue-service/queue/README.md @@ -13,7 +13,7 @@ This module deploys a Storage Account Queue. | 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.Storage/storageAccounts/queueServices/queues` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/queueServices/queues) | +| `Microsoft.Storage/storageAccounts/queueServices/queues` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/queueServices/queues) | ## Parameters diff --git a/avm/res/storage/storage-account/queue-service/queue/main.json b/avm/res/storage/storage-account/queue-service/queue/main.json index 8bea12f90e..2aad9fefb2 100644 --- a/avm/res/storage/storage-account/queue-service/queue/main.json +++ b/avm/res/storage/storage-account/queue-service/queue/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1992900679572007532" + "version": "0.30.23.60470", + "templateHash": "6090221832347220924" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue.", diff --git a/avm/res/storage/storage-account/table-service/README.md b/avm/res/storage/storage-account/table-service/README.md index f4a529a253..7f65d890d0 100644 --- a/avm/res/storage/storage-account/table-service/README.md +++ b/avm/res/storage/storage-account/table-service/README.md @@ -14,8 +14,8 @@ This module deploys a Storage Account Table Service. | :-- | :-- | | `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.Storage/storageAccounts/tableServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/tableServices) | -| `Microsoft.Storage/storageAccounts/tableServices/tables` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/tableServices/tables) | +| `Microsoft.Storage/storageAccounts/tableServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/tableServices) | +| `Microsoft.Storage/storageAccounts/tableServices/tables` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/tableServices/tables) | ## Parameters diff --git a/avm/res/storage/storage-account/table-service/main.json b/avm/res/storage/storage-account/table-service/main.json index 5582b11c4a..df8cac0dbd 100644 --- a/avm/res/storage/storage-account/table-service/main.json +++ b/avm/res/storage/storage-account/table-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15143318143658591417" + "version": "0.30.23.60470", + "templateHash": "6657632516379685259" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service.", @@ -247,8 +247,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "16017327978473583176" + "version": "0.30.23.60470", + "templateHash": "7397003163362434404" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table.", diff --git a/avm/res/storage/storage-account/table-service/table/README.md b/avm/res/storage/storage-account/table-service/table/README.md index 4009c666b3..63e5d835bf 100644 --- a/avm/res/storage/storage-account/table-service/table/README.md +++ b/avm/res/storage/storage-account/table-service/table/README.md @@ -13,7 +13,7 @@ This module deploys a Storage Account Table. | 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.Storage/storageAccounts/tableServices/tables` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/tableServices/tables) | +| `Microsoft.Storage/storageAccounts/tableServices/tables` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/tableServices/tables) | ## Parameters diff --git a/avm/res/storage/storage-account/table-service/table/main.json b/avm/res/storage/storage-account/table-service/table/main.json index 0476ee247e..15dc63d03b 100644 --- a/avm/res/storage/storage-account/table-service/table/main.json +++ b/avm/res/storage/storage-account/table-service/table/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "16017327978473583176" + "version": "0.30.23.60470", + "templateHash": "7397003163362434404" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table.", From 74f1839e457a394954c18fae201662dfbf8ba15b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:18:34 +0100 Subject: [PATCH 43/57] fix: bump github/codeql-action from 3.25.15 to 3.26.9 (#3360) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.25.15 to 3.26.9.

Commits
  • 461ef6c Merge pull request #2503 from github/update-v3.26.9-f861efb2b
  • 00b1146 Update changelog for v3.26.9
  • f861efb Merge pull request #2498 from github/dependabot/npm_and_yarn/npm-9874b37b58
  • 426821d Merge pull request #2485 from github/dependabot/github_actions/actions-a88a8c...
  • 07e8133 Merge pull request #2501 from github/henrymercer/missing-autobuild-config-error
  • e0a151e Fix inconsistency in autobuild error tracking
  • 6b0ce4e revert eslint-plugin-import to 2.29.1
  • 07fd497 Merge branch 'main' into dependabot/github_actions/actions-a88a8c5a24
  • 2cddcb1 Merge pull request #2499 from github/aeisenberg/no-upload-sarif
  • 6225a95 Don't upload during cancelled jobs
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github/codeql-action&package-manager=github_actions&previous-version=3.25.15&new-version=3.26.9)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/platform.ossf-scorecard.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/platform.ossf-scorecard.yml b/.github/workflows/platform.ossf-scorecard.yml index 5c37e5fc09..386dca42a6 100644 --- a/.github/workflows/platform.ossf-scorecard.yml +++ b/.github/workflows/platform.ossf-scorecard.yml @@ -68,6 +68,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard (optional). # Commenting out will disable upload of results to your repo's Code Scanning dashboard - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 + uses: github/codeql-action/upload-sarif@461ef6c76dfe95d5c364de2f431ddbd31a417628 # v3.26.9 with: sarif_file: results.sarif From 95ee1a36aac870b74568a78361ee68a488d4784c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:22:27 +0100 Subject: [PATCH 44/57] fix: bump actions/upload-artifact from 4.3.5 to 4.4.0 (#3155) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.3.5 to 4.4.0.
Release notes

Sourced from actions/upload-artifact's releases.

v4.4.0

Notice: Breaking Changes :warning:

We will no longer include hidden files and folders by default in the upload-artifact action of this version. This reduces the risk that credentials are accidentally uploaded into artifacts. Customers who need to continue to upload these files can use a new option, include-hidden-files, to continue to do so.

See "Notice of upcoming deprecations and breaking changes in GitHub Actions runners" changelog and this issue for more details.

What's Changed

Full Changelog: https://github.com/actions/upload-artifact/compare/v4.3.6...v4.4.0

v4.3.6

What's Changed

Full Changelog: https://github.com/actions/upload-artifact/compare/v4...v4.3.6

Commits
  • 5076954 Merge pull request #598 from actions/joshmgross/exclude-hidden-files
  • d52396a Add a warning about enabling include-hidden-files
  • 710f362 Remove "merged" from include-hidden-files input description
  • 3b315f2 npm run release again 🙂
  • 3be2180 Remove another trailing comma
  • 453e8d0 Update glob license
  • 0a398c1 npm run release
  • a0c40cf Update to latest @actions/glob and fix tests
  • acb59e4 lint
  • cb6558b Exclude hidden files by default
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/upload-artifact&package-manager=github_actions&previous-version=4.3.5&new-version=4.4.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jack Tracey <41163455+jtracey93@users.noreply.github.com> --- .github/workflows/platform.ossf-scorecard.yml | 2 +- .github/workflows/platform.publish-module-index-json.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/platform.ossf-scorecard.yml b/.github/workflows/platform.ossf-scorecard.yml index 386dca42a6..cfbec6ff1f 100644 --- a/.github/workflows/platform.ossf-scorecard.yml +++ b/.github/workflows/platform.ossf-scorecard.yml @@ -59,7 +59,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: "Upload artifact" - uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: SARIF file path: results.sarif diff --git a/.github/workflows/platform.publish-module-index-json.yml b/.github/workflows/platform.publish-module-index-json.yml index b6a510c1f0..9c140c7102 100644 --- a/.github/workflows/platform.publish-module-index-json.yml +++ b/.github/workflows/platform.publish-module-index-json.yml @@ -74,7 +74,7 @@ jobs: } - name: Upload artifacts - uses: actions/upload-artifact@v4.3.5 + uses: actions/upload-artifact@v4.4.0 with: name: publish-module-index-json-artifacts path: | From abd95ca57517fe7b77b0c756e285c1ad5d029ff5 Mon Sep 17 00:00:00 2001 From: Luke Snoddy <37806411+lsnoddy@users.noreply.github.com> Date: Fri, 27 Sep 2024 06:51:17 -0500 Subject: [PATCH 45/57] fix: Added fixed uksouth and ukwest locations for `avm.res.network.trafficmanagerprofile` (#3354) ## Description This pull request introduces fixed locations (uksouth and ukwest) used to deploy App Service plans in the waf test case due to sku family limitations Fixes #3205 Closes #3205 ## Pipeline Reference | Pipeline | | -------- | [![avm.res.network.trafficmanagerprofile](https://github.com/lsnoddy/bicep-registry-modules/actions/workflows/avm.res.network.trafficmanagerprofile.yml/badge.svg?branch=users%2Flsnoddy%2Ftrafficmanagerprovile)](https://github.com/lsnoddy/bicep-registry-modules/actions/workflows/avm.res.network.trafficmanagerprofile.yml) | | ## Type of Change - [x] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../network/trafficmanagerprofile/README.md | 8 +++--- .../network/trafficmanagerprofile/main.json | 4 +-- .../tests/e2e/waf-aligned/main.test.bicep | 27 ++++++++++--------- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/avm/res/network/trafficmanagerprofile/README.md b/avm/res/network/trafficmanagerprofile/README.md index c7dfa7d65c..11fa33f627 100644 --- a/avm/res/network/trafficmanagerprofile/README.md +++ b/avm/res/network/trafficmanagerprofile/README.md @@ -257,7 +257,7 @@ module trafficmanagerprofile 'br/public:avm/res/network/trafficmanagerprofile:' endpointStatus: 'Enabled' priority: 1 targetResourceId: '' @@ -268,7 +268,7 @@ module trafficmanagerprofile 'br/public:avm/res/network/trafficmanagerprofile:' endpointStatus: 'Enabled' priority: 2 targetResourceId: '' @@ -334,7 +334,7 @@ module trafficmanagerprofile 'br/public:avm/res/network/trafficmanagerprofile:", "endpointStatus": "Enabled", "priority": 1, "targetResourceId": "", @@ -345,7 +345,7 @@ module trafficmanagerprofile 'br/public:avm/res/network/trafficmanagerprofile:", "endpointStatus": "Enabled", "priority": 2, "targetResourceId": "", diff --git a/avm/res/network/trafficmanagerprofile/main.json b/avm/res/network/trafficmanagerprofile/main.json index 275f4d0955..1b1e35e937 100644 --- a/avm/res/network/trafficmanagerprofile/main.json +++ b/avm/res/network/trafficmanagerprofile/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "2090813965996228671" + "version": "0.30.23.60470", + "templateHash": "5539048151819308545" }, "name": "Traffic Manager Profiles", "description": "This module deploys a Traffic Manager Profile.", diff --git a/avm/res/network/trafficmanagerprofile/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/trafficmanagerprofile/tests/e2e/waf-aligned/main.test.bicep index 32d82ac36b..3394fe0e75 100644 --- a/avm/res/network/trafficmanagerprofile/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/network/trafficmanagerprofile/tests/e2e/waf-aligned/main.test.bicep @@ -11,8 +11,11 @@ metadata description = 'This instance deploys the module in alignment with the b @maxLength(90) param resourceGroupName string = 'dep-${namePrefix}-network.trafficmanagerprofiles-${serviceShort}-rg' -@description('Optional. The location to deploy resources to.') -param resourceLocation string = deployment().location +#disable-next-line no-hardcoded-location +var enforcedLocation01 = 'uksouth' + +#disable-next-line no-hardcoded-location +var enforcedLocation02 = 'ukwest' @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 = 'ntmpwaf' @@ -28,21 +31,21 @@ param namePrefix string = '#_namePrefix_#' // ================= resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { name: resourceGroupName - location: resourceLocation + location: enforcedLocation01 } module nestedDependencies 'dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + name: '${uniqueString(deployment().name, enforcedLocation01)}-nestedDependencies' params: { managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' - location: resourceLocation + location: enforcedLocation01 serverFarmName01: 'dep-${namePrefix}-sf-${serviceShort}01' serverFarmName02: 'dep-${namePrefix}-sf-${serviceShort}02' webApp01Name: 'dep-${namePrefix}-wa-${serviceShort}01' webApp02Name: 'dep-${namePrefix}-wa-${serviceShort}02' - location01: 'eastus' - location02: 'westus' + location01: enforcedLocation01 + location02: enforcedLocation02 } } @@ -50,13 +53,13 @@ module nestedDependencies 'dependencies.bicep' = { // =========== module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + name: '${uniqueString(deployment().name, enforcedLocation01)}-diagnosticDependencies' params: { storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' - location: resourceLocation + location: enforcedLocation01 } } @@ -67,7 +70,7 @@ module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/t module testDeployment '../../../main.bicep' = [ for iteration in ['init', 'idem']: { scope: resourceGroup - name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + name: '${uniqueString(deployment().name, enforcedLocation01)}-test-${serviceShort}-${iteration}' params: { name: '${namePrefix}${serviceShort}001' location: 'global' @@ -107,7 +110,7 @@ module testDeployment '../../../main.bicep' = [ targetResourceId: nestedDependencies.outputs.webApp01ResourceId weight: 1 priority: 1 - endpointLocation: 'eastus' + endpointLocation: '${enforcedLocation01}' endpointStatus: 'Enabled' } } @@ -118,7 +121,7 @@ module testDeployment '../../../main.bicep' = [ targetResourceId: nestedDependencies.outputs.webApp02ResourceId weight: 1 priority: 2 - endpointLocation: 'westus' + endpointLocation: '${enforcedLocation02}' endpointStatus: 'Enabled' } } From 0463edbfbac951c9c9f87bebdcb10cdf8691cba6 Mon Sep 17 00:00:00 2001 From: Kris Baranek <20225789+krbar@users.noreply.github.com> Date: Sat, 28 Sep 2024 02:42:31 +0200 Subject: [PATCH 46/57] test: `avm/res/network/service-endpoint-policy` - add RBAC test (#3182) ## Description - added `max` test case with roleAssignments test - moved `locks` test from `waf-aligned` to `max` - removed unnecessary comment lines in test cases ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.network.service-endpoint-policy](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.service-endpoint-policy.yml/badge.svg?branch=users%2Fkrbar%2FsvcEpPolicy)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.network.service-endpoint-policy.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [ ] I'm sure there are no other open Pull Requests for the same update/change - [ ] I have run `Set-AVMModule` locally to generate the supporting module files. - [ ] My corresponding pipelines / checks run clean and green without any errors or warnings Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- .../network/service-endpoint-policy/README.md | 111 +++++++++++++++++- .../tests/e2e/defaults/main.test.bicep | 3 - .../tests/e2e/max/dependencies.bicep | 13 ++ .../tests/e2e/max/main.test.bicep | 87 ++++++++++++++ .../tests/e2e/waf-aligned/main.test.bicep | 7 -- 5 files changed, 206 insertions(+), 15 deletions(-) create mode 100644 avm/res/network/service-endpoint-policy/tests/e2e/max/dependencies.bicep create mode 100644 avm/res/network/service-endpoint-policy/tests/e2e/max/main.test.bicep diff --git a/avm/res/network/service-endpoint-policy/README.md b/avm/res/network/service-endpoint-policy/README.md index 7d1097c4b2..2fb4267d72 100644 --- a/avm/res/network/service-endpoint-policy/README.md +++ b/avm/res/network/service-endpoint-policy/README.md @@ -27,7 +27,8 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br/public:avm/res/network/service-endpoint-policy:`. - [Using only defaults](#example-1-using-only-defaults) -- [WAF-aligned](#example-2-waf-aligned) +- [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) ### Example 1: _Using only defaults_ @@ -77,9 +78,9 @@ module serviceEndpointPolicy 'br/public:avm/res/network/service-endpoint-policy:

-### Example 2: _WAF-aligned_ +### Example 2: _Using large parameter set_ -This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. +This instance deploys the module with most of its features enabled.

@@ -91,13 +92,32 @@ module serviceEndpointPolicy 'br/public:avm/res/network/service-endpoint-policy: name: 'serviceEndpointPolicyDeployment' params: { // Required parameters - name: 'nsepwaf001' + name: 'nsepmax001' // Non-required parameters location: '' lock: { kind: 'CanNotDelete' name: 'myCustomLockName' } + roleAssignments: [ + { + name: '36fbc5db-13e9-4bda-9594-1b1cc9db2d6d' + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + { + name: '' + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' + } + ] tags: { Environment: 'Non-Prod' 'hidden-title': 'This is visible in the resource name' @@ -121,7 +141,7 @@ module serviceEndpointPolicy 'br/public:avm/res/network/service-endpoint-policy: "parameters": { // Required parameters "name": { - "value": "nsepwaf001" + "value": "nsepmax001" }, // Non-required parameters "location": { @@ -133,6 +153,87 @@ module serviceEndpointPolicy 'br/public:avm/res/network/service-endpoint-policy: "name": "myCustomLockName" } }, + "roleAssignments": { + "value": [ + { + "name": "36fbc5db-13e9-4bda-9594-1b1cc9db2d6d", + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Owner" + }, + { + "name": "", + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

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

+ +via Bicep module + +```bicep +module serviceEndpointPolicy 'br/public:avm/res/network/service-endpoint-policy:' = { + name: 'serviceEndpointPolicyDeployment' + params: { + // Required parameters + name: 'nsepwaf001' + // Non-required parameters + location: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "nsepwaf001" + }, + // Non-required parameters + "location": { + "value": "" + }, "tags": { "value": { "Environment": "Non-Prod", diff --git a/avm/res/network/service-endpoint-policy/tests/e2e/defaults/main.test.bicep b/avm/res/network/service-endpoint-policy/tests/e2e/defaults/main.test.bicep index ba587c2c4d..6a594af1c8 100644 --- a/avm/res/network/service-endpoint-policy/tests/e2e/defaults/main.test.bicep +++ b/avm/res/network/service-endpoint-policy/tests/e2e/defaults/main.test.bicep @@ -9,14 +9,12 @@ metadata description = 'This instance deploys the module with the minimum set of @description('Optional. The name of the resource group to deploy for testing purposes.') @maxLength(90) -// e.g., for a module 'network/private-endpoint' you could use 'dep-dev-network.privateendpoints-${serviceShort}-rg' param resourceGroupName string = 'dep-${namePrefix}-network.serviceendpointpolicy-${serviceShort}-rg' @description('Optional. The location to deploy resources to.') param resourceLocation string = deployment().location @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') -// e.g., for a module 'network/private-endpoint' you could use 'npe' as a prefix and then 'waf' as a suffix for the waf-aligned test param serviceShort string = 'nsepmin' @description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') @@ -42,7 +40,6 @@ module testDeployment '../../../main.bicep' = [ scope: resourceGroup name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' params: { - // You parameters go here name: '${namePrefix}${serviceShort}001' location: resourceLocation } diff --git a/avm/res/network/service-endpoint-policy/tests/e2e/max/dependencies.bicep b/avm/res/network/service-endpoint-policy/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..7b3d4e8fb0 --- /dev/null +++ b/avm/res/network/service-endpoint-policy/tests/e2e/max/dependencies.bicep @@ -0,0 +1,13 @@ +@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@2023-01-31' = { + name: managedIdentityName + location: location +} + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId diff --git a/avm/res/network/service-endpoint-policy/tests/e2e/max/main.test.bicep b/avm/res/network/service-endpoint-policy/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..43504b4599 --- /dev/null +++ b/avm/res/network/service-endpoint-policy/tests/e2e/max/main.test.bicep @@ -0,0 +1,87 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set' +metadata description = 'This instance deploys the module with most of its features enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-network.serviceendpointpolicy-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'nsepmax' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + roleAssignments: [ + { + name: '36fbc5db-13e9-4bda-9594-1b1cc9db2d6d' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + name: guid('Custom seed ${namePrefix}${serviceShort}') + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + } +] diff --git a/avm/res/network/service-endpoint-policy/tests/e2e/waf-aligned/main.test.bicep b/avm/res/network/service-endpoint-policy/tests/e2e/waf-aligned/main.test.bicep index 0076f59669..b2c6f19281 100644 --- a/avm/res/network/service-endpoint-policy/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/network/service-endpoint-policy/tests/e2e/waf-aligned/main.test.bicep @@ -9,14 +9,12 @@ metadata description = 'This instance deploys the module in alignment with the b @description('Optional. The name of the resource group to deploy for testing purposes.') @maxLength(90) -// e.g., for a module 'network/private-endpoint' you could use 'dep-dev-network.privateendpoints-${serviceShort}-rg' param resourceGroupName string = 'dep-${namePrefix}-network.serviceendpointpolicy-${serviceShort}-rg' @description('Optional. The location to deploy resources to.') param resourceLocation string = deployment().location @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') -// e.g., for a module 'network/private-endpoint' you could use 'npe' as a prefix and then 'waf' as a suffix for the waf-aligned test param serviceShort string = 'nsepwaf' @description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') @@ -42,13 +40,8 @@ module testDeployment '../../../main.bicep' = [ scope: resourceGroup name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' params: { - // You parameters go here name: '${namePrefix}${serviceShort}001' location: resourceLocation - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } tags: { 'hidden-title': 'This is visible in the resource name' Environment: 'Non-Prod' From 9ab65b20e7c76dc2cb6fafbaa3ec2a368a9f4675 Mon Sep 17 00:00:00 2001 From: Seif Bassem <38246040+sebassem@users.noreply.github.com> Date: Mon, 30 Sep 2024 09:23:32 +0300 Subject: [PATCH 47/57] fix: Implement resiliency by default (#3370) ## Description Default to zone-redundant SKUs Default to zone-redundant Public IP fixes #3369 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.network.virtual-network-gateway](https://github.com/sebassem/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network-gateway.yml/badge.svg?branch=avm-resiliency-vnetGw)](https://github.com/sebassem/bicep-registry-modules/actions/workflows/avm.res.network.virtual-network-gateway.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [X] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [X] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings --- .../network/virtual-network-gateway/README.md | 170 ++++++++++-------- .../virtual-network-gateway/main.bicep | 27 ++- .../network/virtual-network-gateway/main.json | 17 +- .../tests/e2e/vpn-no-az/main.test.bicep | 3 +- 4 files changed, 115 insertions(+), 102 deletions(-) diff --git a/avm/res/network/virtual-network-gateway/README.md b/avm/res/network/virtual-network-gateway/README.md index 644177396e..85c0d4ad2e 100644 --- a/avm/res/network/virtual-network-gateway/README.md +++ b/avm/res/network/virtual-network-gateway/README.md @@ -62,7 +62,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: } gatewayType: 'Vpn' name: 'nvgavpn001' - skuName: 'VpnGw2AZ' vNetResourceId: '' // Non-required parameters domainNameLabel: [ @@ -74,6 +73,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 2 3 ] + skuName: 'VpnGw2AZ' vpnClientAadConfiguration: { aadAudience: '41b23e61-6c1e-4545-b367-cd054e0ed4b4' aadIssuer: '' @@ -114,9 +114,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "name": { "value": "nvgavpn001" }, - "skuName": { - "value": "VpnGw2AZ" - }, "vNetResourceId": { "value": "" }, @@ -136,6 +133,9 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 3 ] }, + "skuName": { + "value": "VpnGw2AZ" + }, "vpnClientAadConfiguration": { "value": { "aadAudience": "41b23e61-6c1e-4545-b367-cd054e0ed4b4", @@ -178,7 +178,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: } gatewayType: 'Vpn' name: 'nvgaab001' - skuName: 'VpnGw2AZ' vNetResourceId: '' // Non-required parameters allowRemoteVnetTraffic: true @@ -195,6 +194,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 2 3 ] + skuName: 'VpnGw2AZ' vpnGatewayGeneration: 'Generation2' vpnType: 'RouteBased' } @@ -225,9 +225,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "name": { "value": "nvgaab001" }, - "skuName": { - "value": "VpnGw2AZ" - }, "vNetResourceId": { "value": "" }, @@ -262,6 +259,9 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 3 ] }, + "skuName": { + "value": "VpnGw2AZ" + }, "vpnGatewayGeneration": { "value": "Generation2" }, @@ -302,7 +302,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: } gatewayType: 'Vpn' name: 'nvgaaa001' - skuName: 'VpnGw2AZ' vNetResourceId: '' // Non-required parameters allowRemoteVnetTraffic: true @@ -319,6 +318,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 2 3 ] + skuName: 'VpnGw2AZ' vpnGatewayGeneration: 'Generation2' vpnType: 'RouteBased' } @@ -357,9 +357,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "name": { "value": "nvgaaa001" }, - "skuName": { - "value": "VpnGw2AZ" - }, "vNetResourceId": { "value": "" }, @@ -394,6 +391,9 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 3 ] }, + "skuName": { + "value": "VpnGw2AZ" + }, "vpnGatewayGeneration": { "value": "Generation2" }, @@ -426,7 +426,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: } gatewayType: 'Vpn' name: 'nvgaa001' - skuName: 'VpnGw2AZ' vNetResourceId: '' // Non-required parameters allowRemoteVnetTraffic: true @@ -443,6 +442,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 2 3 ] + skuName: 'VpnGw2AZ' vpnGatewayGeneration: 'Generation2' vpnType: 'RouteBased' } @@ -473,9 +473,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "name": { "value": "nvgaa001" }, - "skuName": { - "value": "VpnGw2AZ" - }, "vNetResourceId": { "value": "" }, @@ -510,6 +507,9 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 3 ] }, + "skuName": { + "value": "VpnGw2AZ" + }, "vpnGatewayGeneration": { "value": "Generation2" }, @@ -547,7 +547,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: } gatewayType: 'Vpn' name: 'nvgapb001' - skuName: 'VpnGw2AZ' vNetResourceId: '' // Non-required parameters allowRemoteVnetTraffic: true @@ -564,6 +563,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 2 3 ] + skuName: 'VpnGw2AZ' vpnGatewayGeneration: 'Generation2' vpnType: 'RouteBased' } @@ -599,9 +599,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "name": { "value": "nvgapb001" }, - "skuName": { - "value": "VpnGw2AZ" - }, "vNetResourceId": { "value": "" }, @@ -636,6 +633,9 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 3 ] }, + "skuName": { + "value": "VpnGw2AZ" + }, "vpnGatewayGeneration": { "value": "Generation2" }, @@ -668,7 +668,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: } gatewayType: 'Vpn' name: 'nvgap001' - skuName: 'VpnGw2AZ' vNetResourceId: '' // Non-required parameters allowRemoteVnetTraffic: true @@ -685,6 +684,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 2 3 ] + skuName: 'VpnGw2AZ' vpnGatewayGeneration: 'Generation2' vpnType: 'RouteBased' } @@ -715,9 +715,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "name": { "value": "nvgap001" }, - "skuName": { - "value": "VpnGw2AZ" - }, "vNetResourceId": { "value": "" }, @@ -752,6 +749,9 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 3 ] }, + "skuName": { + "value": "VpnGw2AZ" + }, "vpnGatewayGeneration": { "value": "Generation2" }, @@ -784,7 +784,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: } gatewayType: 'Vpn' name: 'nvgmin001' - skuName: 'VpnGw2AZ' vNetResourceId: '' // Non-required parameters location: '' @@ -793,6 +792,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 2 3 ] + skuName: 'VpnGw2AZ' } } ``` @@ -821,9 +821,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "name": { "value": "nvgmin001" }, - "skuName": { - "value": "VpnGw2AZ" - }, "vNetResourceId": { "value": "" }, @@ -837,6 +834,9 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 2, 3 ] + }, + "skuName": { + "value": "VpnGw2AZ" } } } @@ -864,7 +864,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: } gatewayType: 'ExpressRoute' name: 'nvger001' - skuName: 'ErGw1AZ' vNetResourceId: '' // Non-required parameters domainNameLabel: [ @@ -877,6 +876,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 2 3 ] + skuName: 'ErGw1AZ' } } ``` @@ -905,9 +905,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "name": { "value": "nvger001" }, - "skuName": { - "value": "ErGw1AZ" - }, "vNetResourceId": { "value": "" }, @@ -929,6 +926,9 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 2, 3 ] + }, + "skuName": { + "value": "ErGw1AZ" } } } @@ -965,7 +965,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: } gatewayType: 'Vpn' name: 'nvgmax001' - skuName: 'VpnGw2AZ' vNetResourceId: '' // Non-required parameters allowRemoteVnetTraffic: true @@ -1053,6 +1052,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: roleDefinitionIdOrName: '' } ] + skuName: 'VpnGw2AZ' tags: { Environment: 'Non-Prod' 'hidden-title': 'This is visible in the resource name' @@ -1097,9 +1097,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "name": { "value": "nvgmax001" }, - "skuName": { - "value": "VpnGw2AZ" - }, "vNetResourceId": { "value": "" }, @@ -1213,6 +1210,9 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: } ] }, + "skuName": { + "value": "VpnGw2AZ" + }, "tags": { "value": { "Environment": "Non-Prod", @@ -1252,10 +1252,11 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: } gatewayType: 'Vpn' name: 'nvgnaz001' - skuName: 'VpnGw1' vNetResourceId: '' // Non-required parameters location: '' + publicIpZones: [] + skuName: 'VpnGw1' } } ``` @@ -1284,15 +1285,18 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "name": { "value": "nvgnaz001" }, - "skuName": { - "value": "VpnGw1" - }, "vNetResourceId": { "value": "" }, // Non-required parameters "location": { "value": "" + }, + "publicIpZones": { + "value": [] + }, + "skuName": { + "value": "VpnGw1" } } } @@ -1320,7 +1324,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: } gatewayType: 'Vpn' name: 'nvgvpn001' - skuName: 'VpnGw2AZ' vNetResourceId: '' // Non-required parameters allowRemoteVnetTraffic: true @@ -1337,6 +1340,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 2 3 ] + skuName: 'VpnGw2AZ' vpnGatewayGeneration: 'Generation2' vpnType: 'RouteBased' } @@ -1367,9 +1371,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "name": { "value": "nvgvpn001" }, - "skuName": { - "value": "VpnGw2AZ" - }, "vNetResourceId": { "value": "" }, @@ -1404,6 +1405,9 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 3 ] }, + "skuName": { + "value": "VpnGw2AZ" + }, "vpnGatewayGeneration": { "value": "Generation2" }, @@ -1445,7 +1449,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: } gatewayType: 'Vpn' name: 'nvgmwaf001' - skuName: 'VpnGw2AZ' vNetResourceId: '' // Non-required parameters allowRemoteVnetTraffic: true @@ -1514,6 +1517,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 2 3 ] + skuName: 'VpnGw2AZ' tags: { Environment: 'Non-Prod' 'hidden-title': 'This is visible in the resource name' @@ -1558,9 +1562,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: "name": { "value": "nvgmwaf001" }, - "skuName": { - "value": "VpnGw2AZ" - }, "vNetResourceId": { "value": "" }, @@ -1653,6 +1654,9 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: 3 ] }, + "skuName": { + "value": "VpnGw2AZ" + }, "tags": { "value": { "Environment": "Non-Prod", @@ -1682,7 +1686,6 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: | [`clusterSettings`](#parameter-clustersettings) | object | Specifies one of the following four configurations: Active-Active with (clusterMode = activeActiveBgp) or without (clusterMode = activeActiveNoBgp) BGP, Active-Passive with (clusterMode = activePassiveBgp) or without (clusterMode = activePassiveNoBgp) BGP. | | [`gatewayType`](#parameter-gatewaytype) | string | Specifies the gateway type. E.g. VPN, ExpressRoute. | | [`name`](#parameter-name) | string | Specifies the Virtual Network Gateway name. | -| [`skuName`](#parameter-skuname) | string | The SKU of the Gateway. | | [`vNetResourceId`](#parameter-vnetresourceid) | string | Virtual Network resource ID. | **Optional parameters** @@ -1709,6 +1712,7 @@ module virtualNetworkGateway 'br/public:avm/res/network/virtual-network-gateway: | [`publicIPPrefixResourceId`](#parameter-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`](#parameter-publicipzones) | array | Specifies the zones of the Public IP address. Basic IP SKU does not support Availability Zones. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`skuName`](#parameter-skuname) | string | The SKU of the Gateway. | | [`tags`](#parameter-tags) | object | Tags of the resource. | | [`vpnClientAadConfiguration`](#parameter-vpnclientaadconfiguration) | object | Configuration for AAD Authentication for P2S Tunnel Type, Cannot be configured if clientRootCertData is provided. | | [`vpnClientAddressPoolPrefix`](#parameter-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. | @@ -1743,35 +1747,6 @@ Specifies the Virtual Network Gateway name. - Required: Yes - Type: string -### Parameter: `skuName` - -The SKU of the Gateway. - -- Required: Yes -- Type: string -- Allowed: - ```Bicep - [ - 'Basic' - 'ErGw1AZ' - 'ErGw2AZ' - 'ErGw3AZ' - 'HighPerformance' - 'Standard' - 'UltraPerformance' - 'VpnGw1' - 'VpnGw1AZ' - 'VpnGw2' - 'VpnGw2AZ' - 'VpnGw3' - 'VpnGw3AZ' - 'VpnGw4' - 'VpnGw4AZ' - 'VpnGw5' - 'VpnGw5AZ' - ] - ``` - ### Parameter: `vNetResourceId` Virtual Network resource ID. @@ -2233,7 +2208,14 @@ Specifies the zones of the Public IP address. Basic IP SKU does not support Avai - Required: No - Type: array -- Default: `[]` +- Default: + ```Bicep + [ + 1 + 2 + 3 + ] + ``` ### Parameter: `roleAssignments` @@ -2339,6 +2321,36 @@ The principal type of the assigned principal ID. ] ``` +### Parameter: `skuName` + +The SKU of the Gateway. + +- Required: No +- Type: string +- Default: `[if(equals(parameters('gatewayType'), 'VPN'), 'VpnGw1AZ', 'ErGw1AZ')]` +- Allowed: + ```Bicep + [ + 'Basic' + 'ErGw1AZ' + 'ErGw2AZ' + 'ErGw3AZ' + 'HighPerformance' + 'Standard' + 'UltraPerformance' + 'VpnGw1' + 'VpnGw1AZ' + 'VpnGw2' + 'VpnGw2AZ' + 'VpnGw3' + 'VpnGw3AZ' + 'VpnGw4' + 'VpnGw4AZ' + 'VpnGw5' + 'VpnGw5AZ' + ] + ``` + ### Parameter: `tags` Tags of the resource. diff --git a/avm/res/network/virtual-network-gateway/main.bicep b/avm/res/network/virtual-network-gateway/main.bicep index 15b0247f69..4cf0d87c15 100644 --- a/avm/res/network/virtual-network-gateway/main.bicep +++ b/avm/res/network/virtual-network-gateway/main.bicep @@ -15,7 +15,11 @@ param gatewayPipName string = '${name}-pip1' param publicIPPrefixResourceId string = '' @description('Optional. Specifies the zones of the Public IP address. Basic IP SKU does not support Availability Zones.') -param publicIpZones array = [] +param publicIpZones array = [ + 1 + 2 + 3 +] @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 = [] @@ -35,7 +39,7 @@ param gatewayType string ]) param vpnGatewayGeneration string = 'None' -@description('Required. The SKU of the Gateway.') +@description('Optional. The SKU of the Gateway.') @allowed([ 'Basic' 'VpnGw1' @@ -55,7 +59,7 @@ param vpnGatewayGeneration string = 'None' 'ErGw2AZ' 'ErGw3AZ' ]) -param skuName string +param skuName string = (gatewayType == 'VPN') ? 'VpnGw1AZ' : 'ErGw1AZ' @description('Optional. Specifies the VPN type.') @allowed([ @@ -175,7 +179,6 @@ var bgpSettingsVar = isActiveActive ] } - // Potential IP configurations (active-active vs active-passive) var ipConfiguration = isActiveActive ? [ @@ -554,53 +557,45 @@ type diagnosticSettingType = { }[]? type activePassiveNoBgpType = { - clusterMode: 'activePassiveNoBgp' - } type activeActiveNoBgpType = { - clusterMode: 'activeActiveNoBgp' @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 \'-pip2\' suffix will be appended to the gateway\'s name.') activeGatewayPipName: string? - } type activePassiveBgpType = { - clusterMode: 'activePassiveBgp' @description('Optional. The Autonomous System Number value. If it\'s not provided, a default \'65515\' value will be assigned to the ASN.') @minValue(0) @maxValue(4294967295) - asn: int? + asn: int? @description('Optional. The list of custom BGP IP Address (APIPA) peering addresses which belong to IP configuration.') customBgpIpAddresses: string[]? } type activeActiveBgpType = { - clusterMode: 'activeActiveBgp' @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 \'-pip2\' suffix will be appended to the gateway\'s name.') activeGatewayPipName: string? - + @description('Optional. The Autonomous System Number value. If it\'s not provided, a default \'65515\' value will be assigned to the ASN.') @minValue(0) @maxValue(4294967295) - asn: int? + asn: int? @description('Optional. The list of custom BGP IP Address (APIPA) peering addresses which belong to IP configuration.') customBgpIpAddresses: string[]? - + @description('Optional. The list of the second custom BGP IP Address (APIPA) peering addresses which belong to IP configuration.') secondCustomBgpIpAddresses: string[]? } @discriminator('clusterMode') type clusterSettingType = activeActiveNoBgpType | activeActiveBgpType | activePassiveBgpType | activePassiveNoBgpType - - diff --git a/avm/res/network/virtual-network-gateway/main.json b/avm/res/network/virtual-network-gateway/main.json index 5e1b4bdf4c..5badcfef95 100644 --- a/avm/res/network/virtual-network-gateway/main.json +++ b/avm/res/network/virtual-network-gateway/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "973776533492793692" + "version": "0.30.23.60470", + "templateHash": "6347373659148864152" }, "name": "Virtual Network Gateways", "description": "This module deploys a Virtual Network Gateway.", @@ -388,7 +388,11 @@ }, "publicIpZones": { "type": "array", - "defaultValue": [], + "defaultValue": [ + 1, + 2, + 3 + ], "metadata": { "description": "Optional. Specifies the zones of the Public IP address. Basic IP SKU does not support Availability Zones." } @@ -424,6 +428,7 @@ }, "skuName": { "type": "string", + "defaultValue": "[if(equals(parameters('gatewayType'), 'VPN'), 'VpnGw1AZ', 'ErGw1AZ')]", "allowedValues": [ "Basic", "VpnGw1", @@ -444,7 +449,7 @@ "ErGw3AZ" ], "metadata": { - "description": "Required. The SKU of the Gateway." + "description": "Optional. The SKU of the Gateway." } }, "vpnType": { @@ -1455,8 +1460,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "915174536118171652" + "version": "0.30.23.60470", + "templateHash": "15500017864202979057" }, "name": "VPN Gateway NAT Rules", "description": "This module deploys a Virtual Network Gateway NAT Rule.", diff --git a/avm/res/network/virtual-network-gateway/tests/e2e/vpn-no-az/main.test.bicep b/avm/res/network/virtual-network-gateway/tests/e2e/vpn-no-az/main.test.bicep index 514b8429ab..fc3410af52 100644 --- a/avm/res/network/virtual-network-gateway/tests/e2e/vpn-no-az/main.test.bicep +++ b/avm/res/network/virtual-network-gateway/tests/e2e/vpn-no-az/main.test.bicep @@ -56,8 +56,9 @@ module testDeployment '../../../main.bicep' = [ name: '${namePrefix}${serviceShort}001' skuName: 'VpnGw1' gatewayType: 'Vpn' + publicIpZones: [] vNetResourceId: nestedDependencies.outputs.vnetResourceId - clusterSettings:{ + clusterSettings: { clusterMode: 'activePassiveNoBgp' } } From 720edc039c8590abc785392cc209407c93f8671c Mon Sep 17 00:00:00 2001 From: Seif Bassem <38246040+sebassem@users.noreply.github.com> Date: Mon, 30 Sep 2024 16:12:22 +0300 Subject: [PATCH 48/57] fix: updating virtualNetwork API for LZ network resource (#3366) ## Description Bump the LZ virtual network resource to avoid subnet deletion on PUT requests Fixes #3330 ## Pipeline Reference | Pipeline | | -------- | | [![avm.ptn.lz.sub-vending](https://github.com/sebassem/bicep-registry-modules/actions/workflows/avm.ptn.lz.sub-vending.yml/badge.svg?branch=avm-ptn-sub-vending-network-api)](https://github.com/sebassem/bicep-registry-modules/actions/workflows/avm.ptn.lz.sub-vending.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [ ] Azure Verified Module updates: - [X] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [X] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [X] I'm sure there are no other open Pull Requests for the same update/change - [X] I have run `Set-AVMModule` locally to generate the supporting module files. - [X] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/ptn/lz/sub-vending/README.md | 20 +- avm/ptn/lz/sub-vending/main.json | 1341 ++++++++++++----- .../modules/subResourceWrapper.bicep | 37 +- 3 files changed, 1030 insertions(+), 368 deletions(-) diff --git a/avm/ptn/lz/sub-vending/README.md b/avm/ptn/lz/sub-vending/README.md index d765a73bfc..d1bea9021b 100644 --- a/avm/ptn/lz/sub-vending/README.md +++ b/avm/ptn/lz/sub-vending/README.md @@ -25,9 +25,9 @@ This module deploys a subscription to accelerate deployment of landing zones. Fo | `Microsoft.Network/privateEndpoints` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints) | | `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-04-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Network/virtualHubs/hubVirtualNetworkConnections` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualHubs/hubVirtualNetworkConnections) | -| `Microsoft.Network/virtualNetworks` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualNetworks) | -| `Microsoft.Network/virtualNetworks/subnets` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualNetworks/subnets) | -| `Microsoft.Network/virtualNetworks/virtualNetworkPeerings` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/virtualNetworks/virtualNetworkPeerings) | +| `Microsoft.Network/virtualNetworks` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks) | +| `Microsoft.Network/virtualNetworks/subnets` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks/subnets) | +| `Microsoft.Network/virtualNetworks/virtualNetworkPeerings` | [2024-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/virtualNetworks/virtualNetworkPeerings) | | `Microsoft.Resources/deploymentScripts` | [2023-08-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Resources/2023-08-01/deploymentScripts) | | `Microsoft.Resources/resourceGroups` | [2021-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Resources/2021-04-01/resourceGroups) | | `Microsoft.Resources/tags` | [2019-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Resources/tags) | @@ -35,14 +35,14 @@ This module deploys a subscription to accelerate deployment of landing zones. Fo | `Microsoft.Storage/storageAccounts/blobServices` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices) | | `Microsoft.Storage/storageAccounts/blobServices/containers` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers) | | `Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers/immutabilityPolicies) | -| `Microsoft.Storage/storageAccounts/fileServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/fileServices) | +| `Microsoft.Storage/storageAccounts/fileServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/fileServices) | | `Microsoft.Storage/storageAccounts/fileServices/shares` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-01-01/storageAccounts/fileServices/shares) | -| `Microsoft.Storage/storageAccounts/localUsers` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/localUsers) | +| `Microsoft.Storage/storageAccounts/localUsers` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/localUsers) | | `Microsoft.Storage/storageAccounts/managementPolicies` | [2023-01-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-01-01/storageAccounts/managementPolicies) | -| `Microsoft.Storage/storageAccounts/queueServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/queueServices) | -| `Microsoft.Storage/storageAccounts/queueServices/queues` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/queueServices/queues) | -| `Microsoft.Storage/storageAccounts/tableServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/tableServices) | -| `Microsoft.Storage/storageAccounts/tableServices/tables` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/storageAccounts/tableServices/tables) | +| `Microsoft.Storage/storageAccounts/queueServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/queueServices) | +| `Microsoft.Storage/storageAccounts/queueServices/queues` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/queueServices/queues) | +| `Microsoft.Storage/storageAccounts/tableServices` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/tableServices) | +| `Microsoft.Storage/storageAccounts/tableServices/tables` | [2023-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-04-01/storageAccounts/tableServices/tables) | | `Microsoft.Subscription/aliases` | [2021-10-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Subscription/2021-10-01/aliases) | ## Usage examples @@ -949,7 +949,7 @@ This section gives you an overview of all local-referenced module files (i.e., o | `br/public:avm/ptn/authorization/role-assignment:0.1.0` | Remote reference | | `br/public:avm/res/managed-identity/user-assigned-identity:0.2.2` | Remote reference | | `br/public:avm/res/network/network-security-group:0.3.0` | Remote reference | -| `br/public:avm/res/network/virtual-network:0.1.7` | Remote reference | +| `br/public:avm/res/network/virtual-network:0.4.0` | Remote reference | | `br/public:avm/res/resources/deployment-script:0.2.3` | Remote reference | | `br/public:avm/res/resources/resource-group:0.2.4` | Remote reference | | `br/public:avm/res/storage/storage-account:0.9.1` | Remote reference | diff --git a/avm/ptn/lz/sub-vending/main.json b/avm/ptn/lz/sub-vending/main.json index 35687dd6ea..e42574e3c1 100644 --- a/avm/ptn/lz/sub-vending/main.json +++ b/avm/ptn/lz/sub-vending/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "2409780926109914899" + "version": "0.30.23.60470", + "templateHash": "2343164809013150587" }, "name": "Sub-vending", "description": "This module deploys a subscription to accelerate deployment of landing zones. For more information on how to use it, please visit this [Wiki](https://github.com/Azure/bicep-lz-vending/wiki).", @@ -445,8 +445,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3759867594724381121" + "version": "0.30.23.60470", + "templateHash": "1611270751895734589" } }, "parameters": { @@ -652,8 +652,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "10394721304346895394" + "version": "0.30.23.60470", + "templateHash": "12288692280280036332" }, "name": "`/subResourcesWrapper/deploy.bicep` Parameters", "description": "This module is used by the [`bicep-lz-vending`](https://aka.ms/sub-vending/bicep) module to help orchestrate the deployment", @@ -1028,8 +1028,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "13961984943235030681" + "version": "0.30.23.60470", + "templateHash": "15074465703139369012" } }, "parameters": { @@ -1086,8 +1086,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "6592820624705341105" + "version": "0.30.23.60470", + "templateHash": "15410141635305926698" } }, "parameters": { @@ -1146,8 +1146,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3589833223987550845" + "version": "0.30.23.60470", + "templateHash": "5472979603320584709" } }, "parameters": { @@ -1202,8 +1202,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15687156082548283745" + "version": "0.30.23.60470", + "templateHash": "11343593259864722989" } }, "parameters": { @@ -1280,8 +1280,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "2885361202003966670" + "version": "0.30.23.60470", + "templateHash": "13884963778440627255" } }, "parameters": { @@ -1335,8 +1335,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "4327209924100539632" + "version": "0.30.23.60470", + "templateHash": "4428652978548820109" } }, "parameters": { @@ -1915,8 +1915,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "6592820624705341105" + "version": "0.30.23.60470", + "templateHash": "15410141635305926698" } }, "parameters": { @@ -1975,8 +1975,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3589833223987550845" + "version": "0.30.23.60470", + "templateHash": "5472979603320584709" } }, "parameters": { @@ -2031,8 +2031,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15687156082548283745" + "version": "0.30.23.60470", + "templateHash": "11343593259864722989" } }, "parameters": { @@ -2109,8 +2109,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "2885361202003966670" + "version": "0.30.23.60470", + "templateHash": "13884963778440627255" } }, "parameters": { @@ -2164,8 +2164,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "4327209924100539632" + "version": "0.30.23.60470", + "templateHash": "4428652978548820109" } }, "parameters": { @@ -2285,7 +2285,7 @@ "ddosProtectionPlanResourceId": { "value": "[parameters('virtualNetworkDdosPlanResourceId')]" }, - "peerings": "[if(and(and(and(and(and(and(parameters('virtualNetworkEnabled'), parameters('virtualNetworkPeeringEnabled')), not(empty(variables('hubVirtualNetworkResourceIdChecked')))), not(empty(parameters('virtualNetworkName')))), not(empty(parameters('virtualNetworkAddressSpace')))), not(empty(parameters('virtualNetworkLocation')))), not(empty(parameters('virtualNetworkResourceGroupName')))), createObject('value', createArray(createObject('allowForwardedTraffic', true(), 'allowVirtualNetworkAccess', true(), 'allowGatewayTransit', false(), 'useRemoteGateways', parameters('virtualNetworkUseRemoteGateways'), 'remotePeeringEnabled', parameters('virtualNetworkPeeringEnabled'), 'remoteVirtualNetworkId', variables('hubVirtualNetworkResourceIdChecked'), 'remotePeeringAllowForwardedTraffic', true(), 'remotePeeringAllowVirtualNetworkAccess', true(), 'remotePeeringAllowGatewayTransit', true(), 'remotePeeringUseRemoteGateways', false()))), createObject('value', createArray()))]", + "peerings": "[if(and(and(and(and(and(and(parameters('virtualNetworkEnabled'), parameters('virtualNetworkPeeringEnabled')), not(empty(variables('hubVirtualNetworkResourceIdChecked')))), not(empty(parameters('virtualNetworkName')))), not(empty(parameters('virtualNetworkAddressSpace')))), not(empty(parameters('virtualNetworkLocation')))), not(empty(parameters('virtualNetworkResourceGroupName')))), createObject('value', createArray(createObject('remoteVirtualNetworkResourceId', variables('hubVirtualNetworkResourceIdChecked'), 'allowForwardedTraffic', true(), 'allowVirtualNetworkAccess', true(), 'allowGatewayTransit', false(), 'useRemoteGateways', parameters('virtualNetworkUseRemoteGateways'), 'remotePeeringEnabled', parameters('virtualNetworkPeeringEnabled'), 'remotePeeringAllowForwardedTraffic', true(), 'remotePeeringAllowVirtualNetworkAccess', true(), 'remotePeeringAllowGatewayTransit', true(), 'remotePeeringUseRemoteGateways', false()))), createObject('value', null()))]", "enableTelemetry": { "value": "[parameters('enableTelemetry')]" } @@ -2297,8 +2297,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16637670595978489426" + "version": "0.29.47.4906", + "templateHash": "15949466154563447171" }, "name": "Virtual Networks", "description": "This module deploys a Virtual Network (vNet).", @@ -2335,6 +2335,13 @@ "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -2515,6 +2522,242 @@ } }, "nullable": true + }, + "peeringType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be peer-localVnetName-remoteVnetName." + } + }, + "remoteVirtualNetworkResourceId": { + "type": "string", + "metadata": { + "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." + } + }, + "allowForwardedTraffic": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "allowGatewayTransit": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Do not verify the provisioning state of the remote gateway. Default is true." + } + }, + "useRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "remotePeeringEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Deploy the outbound and the inbound peering." + } + }, + "remotePeeringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the VNET Peering resource in the remove Virtual Network. If not provided, default value will be peer-remoteVnetName-localVnetName." + } + }, + "remotePeeringAllowForwardedTraffic": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "remotePeeringAllowGatewayTransit": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "remotePeeringAllowVirtualNetworkAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "remotePeeringDoNotVerifyRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Do not verify the provisioning state of the remote gateway. Default is true." + } + }, + "remotePeeringUseRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + } + } + }, + "subnetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The Name of the subnet resource." + } + }, + "addressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." + } + }, + "applicationGatewayIPConfigurations": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application gateway IP configurations of virtual network resource." + } + }, + "delegation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The delegation to enable on the subnet." + } + }, + "natGatewayResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the network security group to assign to the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "allowedValues": [ + "", + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "allowedValues": [ + "", + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. enable or disable apply network policies on private link service in the subnet." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "routeTableResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "serviceEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + }, + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty." + } + } + } } }, "parameters": { @@ -2537,32 +2780,48 @@ "description": "Required. An Array of 1 or more IP Address Prefixes for the Virtual Network." } }, + "virtualNetworkBgpCommunity": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The BGP community associated with the virtual network." + } + }, "subnets": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/subnetType" + }, + "nullable": true, "metadata": { "description": "Optional. An Array of subnets to deploy to the Virtual Network." } }, "dnsServers": { "type": "array", - "defaultValue": [], + "items": { + "type": "string" + }, + "nullable": true, "metadata": { "description": "Optional. DNS Servers associated to the Virtual Network." } }, "ddosProtectionPlanResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "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." } }, "peerings": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/peeringType" + }, + "nullable": true, "metadata": { - "description": "Optional. Virtual Network Peerings configurations." + "description": "Optional. Virtual Network Peering configurations." } }, "vnetEncryption": { @@ -2622,15 +2881,29 @@ "metadata": { "description": "Optional. Enable/Disable usage telemetry for module." } + }, + "enableVmProtection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Indicates if VM protection is enabled for all the subnets in the virtual network." + } } }, "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -2638,8 +2911,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-virtualnetwork.{0}.{1}', replace('0.1.7', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-virtualnetwork.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -2657,42 +2930,21 @@ }, "virtualNetwork": { "type": "Microsoft.Network/virtualNetworks", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", "properties": { - "copy": [ - { - "name": "subnets", - "count": "[length(parameters('subnets'))]", - "input": { - "name": "[parameters('subnets')[copyIndex('subnets')].name]", - "properties": { - "addressPrefix": "[parameters('subnets')[copyIndex('subnets')].addressPrefix]", - "addressPrefixes": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'addressPrefixes'), parameters('subnets')[copyIndex('subnets')].addressPrefixes, createArray())]", - "applicationGatewayIPConfigurations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'applicationGatewayIPConfigurations'), parameters('subnets')[copyIndex('subnets')].applicationGatewayIPConfigurations, createArray())]", - "delegations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'delegations'), parameters('subnets')[copyIndex('subnets')].delegations, createArray())]", - "ipAllocations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'ipAllocations'), parameters('subnets')[copyIndex('subnets')].ipAllocations, createArray())]", - "natGateway": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'natGatewayResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].natGatewayResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].natGatewayResourceId), null())]", - "networkSecurityGroup": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'networkSecurityGroupResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].networkSecurityGroupResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].networkSecurityGroupResourceId), null())]", - "privateEndpointNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'privateEndpointNetworkPolicies'), parameters('subnets')[copyIndex('subnets')].privateEndpointNetworkPolicies, null())]", - "privateLinkServiceNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'privateLinkServiceNetworkPolicies'), parameters('subnets')[copyIndex('subnets')].privateLinkServiceNetworkPolicies, null())]", - "routeTable": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'routeTableResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].routeTableResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].routeTableResourceId), null())]", - "serviceEndpoints": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'serviceEndpoints'), parameters('subnets')[copyIndex('subnets')].serviceEndpoints, createArray())]", - "serviceEndpointPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'serviceEndpointPolicies'), parameters('subnets')[copyIndex('subnets')].serviceEndpointPolicies, createArray())]" - } - } - } - ], "addressSpace": { "addressPrefixes": "[parameters('addressPrefixes')]" }, + "bgpCommunities": "[if(not(empty(parameters('virtualNetworkBgpCommunity'))), createObject('virtualNetworkCommunity', parameters('virtualNetworkBgpCommunity')), null())]", "ddosProtectionPlan": "[if(not(empty(parameters('ddosProtectionPlanResourceId'))), createObject('id', parameters('ddosProtectionPlanResourceId')), null())]", "dhcpOptions": "[if(not(empty(parameters('dnsServers'))), createObject('dnsServers', array(parameters('dnsServers'))), null())]", "enableDdosProtection": "[not(empty(parameters('ddosProtectionPlanResourceId')))]", "encryption": "[if(equals(parameters('vnetEncryption'), true()), createObject('enabled', parameters('vnetEncryption'), 'enforcement', parameters('vnetEncryptionEnforcement')), null())]", - "flowTimeoutInMinutes": "[if(not(equals(parameters('flowTimeoutInMinutes'), 0)), parameters('flowTimeoutInMinutes'), null())]" + "flowTimeoutInMinutes": "[if(not(equals(parameters('flowTimeoutInMinutes'), 0)), parameters('flowTimeoutInMinutes'), null())]", + "enableVmProtection": "[parameters('enableVmProtection')]" } }, "virtualNetwork_lock": { @@ -2753,20 +3005,20 @@ "virtualNetwork_roleAssignments": { "copy": { "name": "virtualNetwork_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ "virtualNetwork" @@ -2775,7 +3027,7 @@ "virtualNetwork_subnets": { "copy": { "name": "virtualNetwork_subnets", - "count": "[length(parameters('subnets'))]", + "count": "[length(coalesce(parameters('subnets'), createArray()))]", "mode": "serial", "batchSize": 1 }, @@ -2792,23 +3044,50 @@ "value": "[parameters('name')]" }, "name": { - "value": "[parameters('subnets')[copyIndex()].name]" + "value": "[coalesce(parameters('subnets'), createArray())[copyIndex()].name]" }, "addressPrefix": { - "value": "[parameters('subnets')[copyIndex()].addressPrefix]" - }, - "addressPrefixes": "[if(contains(parameters('subnets')[copyIndex()], 'addressPrefixes'), createObject('value', parameters('subnets')[copyIndex()].addressPrefixes), createObject('value', createArray()))]", - "applicationGatewayIPConfigurations": "[if(contains(parameters('subnets')[copyIndex()], 'applicationGatewayIPConfigurations'), createObject('value', parameters('subnets')[copyIndex()].applicationGatewayIPConfigurations), createObject('value', createArray()))]", - "delegations": "[if(contains(parameters('subnets')[copyIndex()], 'delegations'), createObject('value', parameters('subnets')[copyIndex()].delegations), createObject('value', createArray()))]", - "ipAllocations": "[if(contains(parameters('subnets')[copyIndex()], 'ipAllocations'), createObject('value', parameters('subnets')[copyIndex()].ipAllocations), createObject('value', createArray()))]", - "natGatewayResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'natGatewayResourceId'), createObject('value', parameters('subnets')[copyIndex()].natGatewayResourceId), createObject('value', ''))]", - "networkSecurityGroupResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'networkSecurityGroupResourceId'), createObject('value', parameters('subnets')[copyIndex()].networkSecurityGroupResourceId), createObject('value', ''))]", - "privateEndpointNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'privateEndpointNetworkPolicies'), createObject('value', parameters('subnets')[copyIndex()].privateEndpointNetworkPolicies), createObject('value', ''))]", - "privateLinkServiceNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'privateLinkServiceNetworkPolicies'), createObject('value', parameters('subnets')[copyIndex()].privateLinkServiceNetworkPolicies), createObject('value', ''))]", - "roleAssignments": "[if(contains(parameters('subnets')[copyIndex()], 'roleAssignments'), createObject('value', parameters('subnets')[copyIndex()].roleAssignments), createObject('value', createArray()))]", - "routeTableResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'routeTableResourceId'), createObject('value', parameters('subnets')[copyIndex()].routeTableResourceId), createObject('value', ''))]", - "serviceEndpointPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'serviceEndpointPolicies'), createObject('value', parameters('subnets')[copyIndex()].serviceEndpointPolicies), createObject('value', createArray()))]", - "serviceEndpoints": "[if(contains(parameters('subnets')[copyIndex()], 'serviceEndpoints'), createObject('value', parameters('subnets')[copyIndex()].serviceEndpoints), createObject('value', createArray()))]" + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'addressPrefix')]" + }, + "addressPrefixes": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'addressPrefixes')]" + }, + "applicationGatewayIPConfigurations": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'applicationGatewayIPConfigurations')]" + }, + "delegation": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'delegation')]" + }, + "natGatewayResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'natGatewayResourceId')]" + }, + "networkSecurityGroupResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'networkSecurityGroupResourceId')]" + }, + "privateEndpointNetworkPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'privateEndpointNetworkPolicies')]" + }, + "privateLinkServiceNetworkPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'privateLinkServiceNetworkPolicies')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "routeTableResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'routeTableResourceId')]" + }, + "serviceEndpointPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'serviceEndpointPolicies')]" + }, + "serviceEndpoints": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'serviceEndpoints')]" + }, + "defaultOutboundAccess": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'defaultOutboundAccess')]" + }, + "sharingScope": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'sharingScope')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -2817,8 +3096,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9634407864982934565" + "version": "0.29.47.4906", + "templateHash": "5699372618313647761" }, "name": "Virtual Network Subnets", "description": "This module deploys a Virtual Network Subnet.", @@ -2830,6 +3109,13 @@ "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -2896,7 +3182,7 @@ "name": { "type": "string", "metadata": { - "description": "Optional. The Name of the subnet resource." + "description": "Requird. The Name of the subnet resource." } }, "virtualNetworkName": { @@ -2907,41 +3193,45 @@ }, "addressPrefix": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The address prefix for the subnet." + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." } }, "networkSecurityGroupResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The resource ID of the network security group to assign to the subnet." } }, "routeTableResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The resource ID of the route table to assign to the subnet." } }, "serviceEndpoints": { "type": "array", + "items": { + "type": "string" + }, "defaultValue": [], "metadata": { "description": "Optional. The service endpoints to enable on the subnet." } }, - "delegations": { - "type": "array", - "defaultValue": [], + "delegation": { + "type": "string", + "nullable": true, "metadata": { - "description": "Optional. The delegations to enable on the subnet." + "description": "Optional. The delegation to enable on the subnet." } }, "natGatewayResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." } @@ -2955,7 +3245,7 @@ "" ], "metadata": { - "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + "description": "Optional. Enable or disable apply network policies on private endpoint in the subnet." } }, "privateLinkServiceNetworkPolicies": { @@ -2967,28 +3257,42 @@ "" ], "metadata": { - "description": "Optional. enable or disable apply network policies on private link service in the subnet." + "description": "Optional. Enable or disable apply network policies on private link service in the subnet." } }, "addressPrefixes": { "type": "array", - "defaultValue": [], + "items": { + "type": "string" + }, + "nullable": true, "metadata": { - "description": "Optional. List of address prefixes for the subnet." + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." } }, - "applicationGatewayIPConfigurations": { - "type": "array", - "defaultValue": [], + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, "metadata": { - "description": "Optional. Application gateway IP configurations of virtual network resource." + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + }, + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty." } }, - "ipAllocations": { + "applicationGatewayIPConfigurations": { "type": "array", "defaultValue": [], "metadata": { - "description": "Optional. Array of IpAllocation which reference this subnet." + "description": "Optional. Application gateway IP configurations of virtual network resource." } }, "serviceEndpointPolicies": { @@ -3006,12 +3310,19 @@ } }, "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -3019,26 +3330,35 @@ "virtualNetwork": { "existing": true, "type": "Microsoft.Network/virtualNetworks", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[parameters('virtualNetworkName')]" }, "subnet": { "type": "Microsoft.Network/virtualNetworks/subnets", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('name'))]", "properties": { + "copy": [ + { + "name": "serviceEndpoints", + "count": "[length(parameters('serviceEndpoints'))]", + "input": { + "service": "[parameters('serviceEndpoints')[copyIndex('serviceEndpoints')]]" + } + } + ], "addressPrefix": "[parameters('addressPrefix')]", + "addressPrefixes": "[parameters('addressPrefixes')]", "networkSecurityGroup": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('id', parameters('networkSecurityGroupResourceId')), null())]", "routeTable": "[if(not(empty(parameters('routeTableResourceId'))), createObject('id', parameters('routeTableResourceId')), null())]", "natGateway": "[if(not(empty(parameters('natGatewayResourceId'))), createObject('id', parameters('natGatewayResourceId')), null())]", - "serviceEndpoints": "[parameters('serviceEndpoints')]", - "delegations": "[parameters('delegations')]", + "delegations": "[if(not(empty(parameters('delegation'))), createArray(createObject('name', parameters('delegation'), 'properties', createObject('serviceName', parameters('delegation')))), createArray())]", "privateEndpointNetworkPolicies": "[if(not(empty(parameters('privateEndpointNetworkPolicies'))), parameters('privateEndpointNetworkPolicies'), null())]", "privateLinkServiceNetworkPolicies": "[if(not(empty(parameters('privateLinkServiceNetworkPolicies'))), parameters('privateLinkServiceNetworkPolicies'), null())]", - "addressPrefixes": "[parameters('addressPrefixes')]", "applicationGatewayIPConfigurations": "[parameters('applicationGatewayIPConfigurations')]", - "ipAllocations": "[parameters('ipAllocations')]", - "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]" + "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]", + "defaultOutboundAccess": "[parameters('defaultOutboundAccess')]", + "sharingScope": "[parameters('sharingScope')]" }, "dependsOn": [ "virtualNetwork" @@ -3047,20 +3367,20 @@ "subnet_roleAssignments": { "copy": { "name": "subnet_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/virtualNetworks/{0}/subnets/{1}', parameters('virtualNetworkName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ "subnet" @@ -3089,19 +3409,19 @@ }, "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name'))]" }, - "subnetAddressPrefix": { + "addressPrefix": { "type": "string", "metadata": { "description": "The address prefix for the subnet." }, - "value": "[reference('subnet').addressPrefix]" + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefix'), '')]" }, - "subnetAddressPrefixes": { + "addressPrefixes": { "type": "array", "metadata": { "description": "List of address prefixes for the subnet." }, - "value": "[if(not(empty(parameters('addressPrefixes'))), reference('subnet').addressPrefixes, createArray())]" + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefixes'), createArray())]" } } } @@ -3113,7 +3433,7 @@ "virtualNetwork_peering_local": { "copy": { "name": "virtualNetwork_peering_local", - "count": "[length(parameters('peerings'))]" + "count": "[length(coalesce(parameters('peerings'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -3127,15 +3447,27 @@ "localVnetName": { "value": "[parameters('name')]" }, - "remoteVirtualNetworkId": { - "value": "[parameters('peerings')[copyIndex()].remoteVirtualNetworkId]" + "remoteVirtualNetworkResourceId": { + "value": "[coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId]" + }, + "name": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'name')]" + }, + "allowForwardedTraffic": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowForwardedTraffic')]" + }, + "allowGatewayTransit": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowGatewayTransit')]" + }, + "allowVirtualNetworkAccess": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowVirtualNetworkAccess')]" + }, + "doNotVerifyRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'doNotVerifyRemoteGateways')]" }, - "name": "[if(contains(parameters('peerings')[copyIndex()], 'name'), createObject('value', parameters('peerings')[copyIndex()].name), createObject('value', format('{0}-{1}', parameters('name'), last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')))))]", - "allowForwardedTraffic": "[if(contains(parameters('peerings')[copyIndex()], 'allowForwardedTraffic'), createObject('value', parameters('peerings')[copyIndex()].allowForwardedTraffic), createObject('value', true()))]", - "allowGatewayTransit": "[if(contains(parameters('peerings')[copyIndex()], 'allowGatewayTransit'), createObject('value', parameters('peerings')[copyIndex()].allowGatewayTransit), createObject('value', false()))]", - "allowVirtualNetworkAccess": "[if(contains(parameters('peerings')[copyIndex()], 'allowVirtualNetworkAccess'), createObject('value', parameters('peerings')[copyIndex()].allowVirtualNetworkAccess), createObject('value', true()))]", - "doNotVerifyRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'doNotVerifyRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].doNotVerifyRemoteGateways), createObject('value', true()))]", - "useRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'useRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].useRemoteGateways), createObject('value', false()))]" + "useRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'useRemoteGateways')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -3143,8 +3475,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "39994426069187924" + "version": "0.29.47.4906", + "templateHash": "5206620163504251868" }, "name": "Virtual Network Peerings", "description": "This module deploys a Virtual Network Peering.", @@ -3153,9 +3485,9 @@ "parameters": { "name": { "type": "string", - "defaultValue": "[format('{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkId'), '/')))]", + "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]", "metadata": { - "description": "Optional. The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName." } }, "localVnetName": { @@ -3164,7 +3496,7 @@ "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." } }, - "remoteVirtualNetworkId": { + "remoteVirtualNetworkResourceId": { "type": "string", "metadata": { "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." @@ -3209,7 +3541,7 @@ "resources": [ { "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", "properties": { "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", @@ -3218,7 +3550,7 @@ "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", "useRemoteGateways": "[parameters('useRemoteGateways')]", "remoteVirtualNetwork": { - "id": "[parameters('remoteVirtualNetworkId')]" + "id": "[parameters('remoteVirtualNetworkResourceId')]" } } } @@ -3255,14 +3587,14 @@ "virtualNetwork_peering_remote": { "copy": { "name": "virtualNetwork_peering_remote", - "count": "[length(parameters('peerings'))]" + "count": "[length(coalesce(parameters('peerings'), createArray()))]" }, - "condition": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringEnabled'), equals(parameters('peerings')[copyIndex()].remotePeeringEnabled, true()), false())]", + "condition": "[coalesce(tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringEnabled'), false())]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-virtualNetworkPeering-remote-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')[2]]", - "resourceGroup": "[split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')[4]]", + "subscriptionId": "[split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/')[2]]", + "resourceGroup": "[split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -3270,17 +3602,29 @@ "mode": "Incremental", "parameters": { "localVnetName": { - "value": "[last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/'))]" + "value": "[last(split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/'))]" }, - "remoteVirtualNetworkId": { + "remoteVirtualNetworkResourceId": { "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" }, - "name": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringName'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringName), createObject('value', format('{0}-{1}', last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')), parameters('name'))))]", - "allowForwardedTraffic": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowForwardedTraffic'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowForwardedTraffic), createObject('value', true()))]", - "allowGatewayTransit": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowGatewayTransit'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowGatewayTransit), createObject('value', false()))]", - "allowVirtualNetworkAccess": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowVirtualNetworkAccess'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowVirtualNetworkAccess), createObject('value', true()))]", - "doNotVerifyRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringDoNotVerifyRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringDoNotVerifyRemoteGateways), createObject('value', true()))]", - "useRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringUseRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringUseRemoteGateways), createObject('value', false()))]" + "name": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringName')]" + }, + "allowForwardedTraffic": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowForwardedTraffic')]" + }, + "allowGatewayTransit": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowGatewayTransit')]" + }, + "allowVirtualNetworkAccess": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowVirtualNetworkAccess')]" + }, + "doNotVerifyRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringDoNotVerifyRemoteGateways')]" + }, + "useRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringUseRemoteGateways')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -3288,8 +3632,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "39994426069187924" + "version": "0.29.47.4906", + "templateHash": "5206620163504251868" }, "name": "Virtual Network Peerings", "description": "This module deploys a Virtual Network Peering.", @@ -3298,9 +3642,9 @@ "parameters": { "name": { "type": "string", - "defaultValue": "[format('{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkId'), '/')))]", + "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]", "metadata": { - "description": "Optional. The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName." } }, "localVnetName": { @@ -3309,7 +3653,7 @@ "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." } }, - "remoteVirtualNetworkId": { + "remoteVirtualNetworkResourceId": { "type": "string", "metadata": { "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." @@ -3354,7 +3698,7 @@ "resources": [ { "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", "properties": { "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", @@ -3363,7 +3707,7 @@ "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", "useRemoteGateways": "[parameters('useRemoteGateways')]", "remoteVirtualNetwork": { - "id": "[parameters('remoteVirtualNetworkId')]" + "id": "[parameters('remoteVirtualNetworkResourceId')]" } } } @@ -3426,8 +3770,8 @@ "description": "The names of the deployed subnets." }, "copy": { - "count": "[length(parameters('subnets'))]", - "input": "[parameters('subnets')[copyIndex()].name]" + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "input": "[reference(format('virtualNetwork_subnets[{0}]', copyIndex())).outputs.name.value]" } }, "subnetResourceIds": { @@ -3436,8 +3780,8 @@ "description": "The resource IDs of the deployed subnets." }, "copy": { - "count": "[length(parameters('subnets'))]", - "input": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('name'), parameters('subnets')[copyIndex()].name)]" + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "input": "[reference(format('virtualNetwork_subnets[{0}]', copyIndex())).outputs.resourceId.value]" } }, "location": { @@ -3445,7 +3789,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('virtualNetwork', '2023-11-01', 'full').location]" + "value": "[reference('virtualNetwork', '2024-01-01', 'full').location]" } } } @@ -3487,8 +3831,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3320668363507906643" + "version": "0.30.23.60470", + "templateHash": "15250207882926040999" } }, "parameters": { @@ -13299,28 +13643,7 @@ "[parameters('virtualNetworkDeploymentScriptAddressPrefix')]" ] }, - "subnets": { - "value": [ - { - "addressPrefix": "[if(not(empty(parameters('resourceProviders'))), cidrSubnet(parameters('virtualNetworkDeploymentScriptAddressPrefix'), 24, 0), null())]", - "name": "ds-subnet-001", - "networkSecurityGroupResourceId": "[if(not(empty(parameters('resourceProviders'))), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('deploymentScriptResourceGroupName')), 'Microsoft.Resources/deployments', variables('deploymentNames').createDsNsg), '2022-09-01').outputs.resourceId.value, null())]", - "serviceEndpoints": [ - { - "service": "Microsoft.Storage" - } - ], - "delegations": [ - { - "name": "Microsoft.ContainerInstance.containerGroups", - "properties": { - "serviceName": "Microsoft.ContainerInstance/containerGroups" - } - } - ] - } - ] - }, + "subnets": "[if(not(empty(parameters('resourceProviders'))), createObject('value', createArray(createObject('addressPrefix', if(not(empty(parameters('resourceProviders'))), cidrSubnet(parameters('virtualNetworkDeploymentScriptAddressPrefix'), 24, 0), null()), 'name', 'ds-subnet-001', 'networkSecurityGroupResourceId', if(not(empty(parameters('resourceProviders'))), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('deploymentScriptResourceGroupName')), 'Microsoft.Resources/deployments', variables('deploymentNames').createDsNsg), '2022-09-01').outputs.resourceId.value, null()), 'serviceEndpoints', createArray('Microsoft.Storage'), 'delegation', 'Microsoft.ContainerInstance/containerGroups'))), createObject('value', null()))]", "enableTelemetry": { "value": "[parameters('enableTelemetry')]" } @@ -13332,8 +13655,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "16637670595978489426" + "version": "0.29.47.4906", + "templateHash": "15949466154563447171" }, "name": "Virtual Networks", "description": "This module deploys a Virtual Network (vNet).", @@ -13370,6 +13693,13 @@ "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -13550,6 +13880,242 @@ } }, "nullable": true + }, + "peeringType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be peer-localVnetName-remoteVnetName." + } + }, + "remoteVirtualNetworkResourceId": { + "type": "string", + "metadata": { + "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." + } + }, + "allowForwardedTraffic": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "allowGatewayTransit": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "allowVirtualNetworkAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "doNotVerifyRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Do not verify the provisioning state of the remote gateway. Default is true." + } + }, + "useRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "remotePeeringEnabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Deploy the outbound and the inbound peering." + } + }, + "remotePeeringName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the VNET Peering resource in the remove Virtual Network. If not provided, default value will be peer-remoteVnetName-localVnetName." + } + }, + "remotePeeringAllowForwardedTraffic": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "remotePeeringAllowGatewayTransit": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If gateway links can be used in remote virtual networking to link to this virtual network. Default is false." + } + }, + "remotePeeringAllowVirtualNetworkAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + }, + "remotePeeringDoNotVerifyRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Do not verify the provisioning state of the remote gateway. Default is true." + } + }, + "remotePeeringUseRemoteGateways": { + "type": "bool", + "nullable": true, + "metadata": { + "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." + } + } + } + }, + "subnetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The Name of the subnet resource." + } + }, + "addressPrefix": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." + } + }, + "addressPrefixes": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." + } + }, + "applicationGatewayIPConfigurations": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application gateway IP configurations of virtual network resource." + } + }, + "delegation": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The delegation to enable on the subnet." + } + }, + "natGatewayResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." + } + }, + "networkSecurityGroupResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the network security group to assign to the subnet." + } + }, + "privateEndpointNetworkPolicies": { + "type": "string", + "allowedValues": [ + "", + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + } + }, + "privateLinkServiceNetworkPolicies": { + "type": "string", + "allowedValues": [ + "", + "Disabled", + "Enabled" + ], + "nullable": true, + "metadata": { + "description": "Optional. enable or disable apply network policies on private link service in the subnet." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "routeTableResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The resource ID of the route table to assign to the subnet." + } + }, + "serviceEndpointPolicies": { + "type": "array", + "items": { + "type": "object" + }, + "nullable": true, + "metadata": { + "description": "Optional. An array of service endpoint policies." + } + }, + "serviceEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The service endpoints to enable on the subnet." + } + }, + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." + } + }, + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty." + } + } + } } }, "parameters": { @@ -13572,32 +14138,48 @@ "description": "Required. An Array of 1 or more IP Address Prefixes for the Virtual Network." } }, + "virtualNetworkBgpCommunity": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The BGP community associated with the virtual network." + } + }, "subnets": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/subnetType" + }, + "nullable": true, "metadata": { "description": "Optional. An Array of subnets to deploy to the Virtual Network." } }, "dnsServers": { "type": "array", - "defaultValue": [], + "items": { + "type": "string" + }, + "nullable": true, "metadata": { "description": "Optional. DNS Servers associated to the Virtual Network." } }, "ddosProtectionPlanResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "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." } }, "peerings": { "type": "array", - "defaultValue": [], + "items": { + "$ref": "#/definitions/peeringType" + }, + "nullable": true, "metadata": { - "description": "Optional. Virtual Network Peerings configurations." + "description": "Optional. Virtual Network Peering configurations." } }, "vnetEncryption": { @@ -13657,15 +14239,29 @@ "metadata": { "description": "Optional. Enable/Disable usage telemetry for module." } + }, + "enableVmProtection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Indicates if VM protection is enabled for all the subnets in the virtual network." + } } }, "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -13673,8 +14269,8 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2023-07-01", - "name": "[format('46d3xbcp.res.network-virtualnetwork.{0}.{1}', replace('0.1.7', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-virtualnetwork.{0}.{1}', replace('0.4.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -13692,42 +14288,21 @@ }, "virtualNetwork": { "type": "Microsoft.Network/virtualNetworks", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", "properties": { - "copy": [ - { - "name": "subnets", - "count": "[length(parameters('subnets'))]", - "input": { - "name": "[parameters('subnets')[copyIndex('subnets')].name]", - "properties": { - "addressPrefix": "[parameters('subnets')[copyIndex('subnets')].addressPrefix]", - "addressPrefixes": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'addressPrefixes'), parameters('subnets')[copyIndex('subnets')].addressPrefixes, createArray())]", - "applicationGatewayIPConfigurations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'applicationGatewayIPConfigurations'), parameters('subnets')[copyIndex('subnets')].applicationGatewayIPConfigurations, createArray())]", - "delegations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'delegations'), parameters('subnets')[copyIndex('subnets')].delegations, createArray())]", - "ipAllocations": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'ipAllocations'), parameters('subnets')[copyIndex('subnets')].ipAllocations, createArray())]", - "natGateway": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'natGatewayResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].natGatewayResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].natGatewayResourceId), null())]", - "networkSecurityGroup": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'networkSecurityGroupResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].networkSecurityGroupResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].networkSecurityGroupResourceId), null())]", - "privateEndpointNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'privateEndpointNetworkPolicies'), parameters('subnets')[copyIndex('subnets')].privateEndpointNetworkPolicies, null())]", - "privateLinkServiceNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'privateLinkServiceNetworkPolicies'), parameters('subnets')[copyIndex('subnets')].privateLinkServiceNetworkPolicies, null())]", - "routeTable": "[if(and(contains(parameters('subnets')[copyIndex('subnets')], 'routeTableResourceId'), not(empty(parameters('subnets')[copyIndex('subnets')].routeTableResourceId))), createObject('id', parameters('subnets')[copyIndex('subnets')].routeTableResourceId), null())]", - "serviceEndpoints": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'serviceEndpoints'), parameters('subnets')[copyIndex('subnets')].serviceEndpoints, createArray())]", - "serviceEndpointPolicies": "[if(contains(parameters('subnets')[copyIndex('subnets')], 'serviceEndpointPolicies'), parameters('subnets')[copyIndex('subnets')].serviceEndpointPolicies, createArray())]" - } - } - } - ], "addressSpace": { "addressPrefixes": "[parameters('addressPrefixes')]" }, + "bgpCommunities": "[if(not(empty(parameters('virtualNetworkBgpCommunity'))), createObject('virtualNetworkCommunity', parameters('virtualNetworkBgpCommunity')), null())]", "ddosProtectionPlan": "[if(not(empty(parameters('ddosProtectionPlanResourceId'))), createObject('id', parameters('ddosProtectionPlanResourceId')), null())]", "dhcpOptions": "[if(not(empty(parameters('dnsServers'))), createObject('dnsServers', array(parameters('dnsServers'))), null())]", "enableDdosProtection": "[not(empty(parameters('ddosProtectionPlanResourceId')))]", "encryption": "[if(equals(parameters('vnetEncryption'), true()), createObject('enabled', parameters('vnetEncryption'), 'enforcement', parameters('vnetEncryptionEnforcement')), null())]", - "flowTimeoutInMinutes": "[if(not(equals(parameters('flowTimeoutInMinutes'), 0)), parameters('flowTimeoutInMinutes'), null())]" + "flowTimeoutInMinutes": "[if(not(equals(parameters('flowTimeoutInMinutes'), 0)), parameters('flowTimeoutInMinutes'), null())]", + "enableVmProtection": "[parameters('enableVmProtection')]" } }, "virtualNetwork_lock": { @@ -13788,20 +14363,20 @@ "virtualNetwork_roleAssignments": { "copy": { "name": "virtualNetwork_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ "virtualNetwork" @@ -13810,7 +14385,7 @@ "virtualNetwork_subnets": { "copy": { "name": "virtualNetwork_subnets", - "count": "[length(parameters('subnets'))]", + "count": "[length(coalesce(parameters('subnets'), createArray()))]", "mode": "serial", "batchSize": 1 }, @@ -13827,23 +14402,50 @@ "value": "[parameters('name')]" }, "name": { - "value": "[parameters('subnets')[copyIndex()].name]" + "value": "[coalesce(parameters('subnets'), createArray())[copyIndex()].name]" }, "addressPrefix": { - "value": "[parameters('subnets')[copyIndex()].addressPrefix]" - }, - "addressPrefixes": "[if(contains(parameters('subnets')[copyIndex()], 'addressPrefixes'), createObject('value', parameters('subnets')[copyIndex()].addressPrefixes), createObject('value', createArray()))]", - "applicationGatewayIPConfigurations": "[if(contains(parameters('subnets')[copyIndex()], 'applicationGatewayIPConfigurations'), createObject('value', parameters('subnets')[copyIndex()].applicationGatewayIPConfigurations), createObject('value', createArray()))]", - "delegations": "[if(contains(parameters('subnets')[copyIndex()], 'delegations'), createObject('value', parameters('subnets')[copyIndex()].delegations), createObject('value', createArray()))]", - "ipAllocations": "[if(contains(parameters('subnets')[copyIndex()], 'ipAllocations'), createObject('value', parameters('subnets')[copyIndex()].ipAllocations), createObject('value', createArray()))]", - "natGatewayResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'natGatewayResourceId'), createObject('value', parameters('subnets')[copyIndex()].natGatewayResourceId), createObject('value', ''))]", - "networkSecurityGroupResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'networkSecurityGroupResourceId'), createObject('value', parameters('subnets')[copyIndex()].networkSecurityGroupResourceId), createObject('value', ''))]", - "privateEndpointNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'privateEndpointNetworkPolicies'), createObject('value', parameters('subnets')[copyIndex()].privateEndpointNetworkPolicies), createObject('value', ''))]", - "privateLinkServiceNetworkPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'privateLinkServiceNetworkPolicies'), createObject('value', parameters('subnets')[copyIndex()].privateLinkServiceNetworkPolicies), createObject('value', ''))]", - "roleAssignments": "[if(contains(parameters('subnets')[copyIndex()], 'roleAssignments'), createObject('value', parameters('subnets')[copyIndex()].roleAssignments), createObject('value', createArray()))]", - "routeTableResourceId": "[if(contains(parameters('subnets')[copyIndex()], 'routeTableResourceId'), createObject('value', parameters('subnets')[copyIndex()].routeTableResourceId), createObject('value', ''))]", - "serviceEndpointPolicies": "[if(contains(parameters('subnets')[copyIndex()], 'serviceEndpointPolicies'), createObject('value', parameters('subnets')[copyIndex()].serviceEndpointPolicies), createObject('value', createArray()))]", - "serviceEndpoints": "[if(contains(parameters('subnets')[copyIndex()], 'serviceEndpoints'), createObject('value', parameters('subnets')[copyIndex()].serviceEndpoints), createObject('value', createArray()))]" + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'addressPrefix')]" + }, + "addressPrefixes": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'addressPrefixes')]" + }, + "applicationGatewayIPConfigurations": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'applicationGatewayIPConfigurations')]" + }, + "delegation": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'delegation')]" + }, + "natGatewayResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'natGatewayResourceId')]" + }, + "networkSecurityGroupResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'networkSecurityGroupResourceId')]" + }, + "privateEndpointNetworkPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'privateEndpointNetworkPolicies')]" + }, + "privateLinkServiceNetworkPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'privateLinkServiceNetworkPolicies')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "routeTableResourceId": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'routeTableResourceId')]" + }, + "serviceEndpointPolicies": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'serviceEndpointPolicies')]" + }, + "serviceEndpoints": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'serviceEndpoints')]" + }, + "defaultOutboundAccess": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'defaultOutboundAccess')]" + }, + "sharingScope": { + "value": "[tryGet(coalesce(parameters('subnets'), createArray())[copyIndex()], 'sharingScope')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -13852,8 +14454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "9634407864982934565" + "version": "0.29.47.4906", + "templateHash": "5699372618313647761" }, "name": "Virtual Network Subnets", "description": "This module deploys a Virtual Network Subnet.", @@ -13865,6 +14467,13 @@ "items": { "type": "object", "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, "roleDefinitionIdOrName": { "type": "string", "metadata": { @@ -13931,7 +14540,7 @@ "name": { "type": "string", "metadata": { - "description": "Optional. The Name of the subnet resource." + "description": "Requird. The Name of the subnet resource." } }, "virtualNetworkName": { @@ -13942,41 +14551,45 @@ }, "addressPrefix": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The address prefix for the subnet." + "description": "Conditional. The address prefix for the subnet. Required if `addressPrefixes` is empty." } }, "networkSecurityGroupResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The resource ID of the network security group to assign to the subnet." } }, "routeTableResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The resource ID of the route table to assign to the subnet." } }, "serviceEndpoints": { "type": "array", + "items": { + "type": "string" + }, "defaultValue": [], "metadata": { "description": "Optional. The service endpoints to enable on the subnet." } }, - "delegations": { - "type": "array", - "defaultValue": [], + "delegation": { + "type": "string", + "nullable": true, "metadata": { - "description": "Optional. The delegations to enable on the subnet." + "description": "Optional. The delegation to enable on the subnet." } }, "natGatewayResourceId": { "type": "string", - "defaultValue": "", + "nullable": true, "metadata": { "description": "Optional. The resource ID of the NAT Gateway to use for the subnet." } @@ -13990,7 +14603,7 @@ "" ], "metadata": { - "description": "Optional. enable or disable apply network policies on private endpoint in the subnet." + "description": "Optional. Enable or disable apply network policies on private endpoint in the subnet." } }, "privateLinkServiceNetworkPolicies": { @@ -14002,28 +14615,42 @@ "" ], "metadata": { - "description": "Optional. enable or disable apply network policies on private link service in the subnet." + "description": "Optional. Enable or disable apply network policies on private link service in the subnet." } }, "addressPrefixes": { "type": "array", - "defaultValue": [], + "items": { + "type": "string" + }, + "nullable": true, "metadata": { - "description": "Optional. List of address prefixes for the subnet." + "description": "Conditional. List of address prefixes for the subnet. Required if `addressPrefix` is empty." } }, - "applicationGatewayIPConfigurations": { - "type": "array", - "defaultValue": [], + "defaultOutboundAccess": { + "type": "bool", + "nullable": true, "metadata": { - "description": "Optional. Application gateway IP configurations of virtual network resource." + "description": "Optional. Set this property to false to disable default outbound connectivity for all VMs in the subnet. This property can only be set at the time of subnet creation and cannot be updated for an existing subnet." } }, - "ipAllocations": { + "sharingScope": { + "type": "string", + "allowedValues": [ + "DelegatedServices", + "Tenant" + ], + "nullable": true, + "metadata": { + "description": "Optional. Set this property to Tenant to allow sharing subnet with other subscriptions in your AAD tenant. This property can only be set if defaultOutboundAccess is set to false, both properties can only be set if subnet is empty." + } + }, + "applicationGatewayIPConfigurations": { "type": "array", "defaultValue": [], "metadata": { - "description": "Optional. Array of IpAllocation which reference this subnet." + "description": "Optional. Application gateway IP configurations of virtual network resource." } }, "serviceEndpointPolicies": { @@ -14041,12 +14668,19 @@ } }, "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], "builtInRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", - "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" } }, @@ -14054,26 +14688,35 @@ "virtualNetwork": { "existing": true, "type": "Microsoft.Network/virtualNetworks", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[parameters('virtualNetworkName')]" }, "subnet": { "type": "Microsoft.Network/virtualNetworks/subnets", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('name'))]", "properties": { + "copy": [ + { + "name": "serviceEndpoints", + "count": "[length(parameters('serviceEndpoints'))]", + "input": { + "service": "[parameters('serviceEndpoints')[copyIndex('serviceEndpoints')]]" + } + } + ], "addressPrefix": "[parameters('addressPrefix')]", + "addressPrefixes": "[parameters('addressPrefixes')]", "networkSecurityGroup": "[if(not(empty(parameters('networkSecurityGroupResourceId'))), createObject('id', parameters('networkSecurityGroupResourceId')), null())]", "routeTable": "[if(not(empty(parameters('routeTableResourceId'))), createObject('id', parameters('routeTableResourceId')), null())]", "natGateway": "[if(not(empty(parameters('natGatewayResourceId'))), createObject('id', parameters('natGatewayResourceId')), null())]", - "serviceEndpoints": "[parameters('serviceEndpoints')]", - "delegations": "[parameters('delegations')]", + "delegations": "[if(not(empty(parameters('delegation'))), createArray(createObject('name', parameters('delegation'), 'properties', createObject('serviceName', parameters('delegation')))), createArray())]", "privateEndpointNetworkPolicies": "[if(not(empty(parameters('privateEndpointNetworkPolicies'))), parameters('privateEndpointNetworkPolicies'), null())]", "privateLinkServiceNetworkPolicies": "[if(not(empty(parameters('privateLinkServiceNetworkPolicies'))), parameters('privateLinkServiceNetworkPolicies'), null())]", - "addressPrefixes": "[parameters('addressPrefixes')]", "applicationGatewayIPConfigurations": "[parameters('applicationGatewayIPConfigurations')]", - "ipAllocations": "[parameters('ipAllocations')]", - "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]" + "serviceEndpointPolicies": "[parameters('serviceEndpointPolicies')]", + "defaultOutboundAccess": "[parameters('defaultOutboundAccess')]", + "sharingScope": "[parameters('sharingScope')]" }, "dependsOn": [ "virtualNetwork" @@ -14082,20 +14725,20 @@ "subnet_roleAssignments": { "copy": { "name": "subnet_roleAssignments", - "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]" + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", "scope": "[format('Microsoft.Network/virtualNetworks/{0}/subnets/{1}', parameters('virtualNetworkName'), parameters('name'))]", - "name": "[guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { - "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]", - "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]", - "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]", - "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]", - "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]", - "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", - "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" }, "dependsOn": [ "subnet" @@ -14124,19 +14767,19 @@ }, "value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name'))]" }, - "subnetAddressPrefix": { + "addressPrefix": { "type": "string", "metadata": { "description": "The address prefix for the subnet." }, - "value": "[reference('subnet').addressPrefix]" + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefix'), '')]" }, - "subnetAddressPrefixes": { + "addressPrefixes": { "type": "array", "metadata": { "description": "List of address prefixes for the subnet." }, - "value": "[if(not(empty(parameters('addressPrefixes'))), reference('subnet').addressPrefixes, createArray())]" + "value": "[coalesce(tryGet(reference('subnet'), 'addressPrefixes'), createArray())]" } } } @@ -14148,7 +14791,7 @@ "virtualNetwork_peering_local": { "copy": { "name": "virtualNetwork_peering_local", - "count": "[length(parameters('peerings'))]" + "count": "[length(coalesce(parameters('peerings'), createArray()))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", @@ -14162,15 +14805,27 @@ "localVnetName": { "value": "[parameters('name')]" }, - "remoteVirtualNetworkId": { - "value": "[parameters('peerings')[copyIndex()].remoteVirtualNetworkId]" + "remoteVirtualNetworkResourceId": { + "value": "[coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId]" + }, + "name": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'name')]" + }, + "allowForwardedTraffic": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowForwardedTraffic')]" + }, + "allowGatewayTransit": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowGatewayTransit')]" + }, + "allowVirtualNetworkAccess": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'allowVirtualNetworkAccess')]" + }, + "doNotVerifyRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'doNotVerifyRemoteGateways')]" }, - "name": "[if(contains(parameters('peerings')[copyIndex()], 'name'), createObject('value', parameters('peerings')[copyIndex()].name), createObject('value', format('{0}-{1}', parameters('name'), last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')))))]", - "allowForwardedTraffic": "[if(contains(parameters('peerings')[copyIndex()], 'allowForwardedTraffic'), createObject('value', parameters('peerings')[copyIndex()].allowForwardedTraffic), createObject('value', true()))]", - "allowGatewayTransit": "[if(contains(parameters('peerings')[copyIndex()], 'allowGatewayTransit'), createObject('value', parameters('peerings')[copyIndex()].allowGatewayTransit), createObject('value', false()))]", - "allowVirtualNetworkAccess": "[if(contains(parameters('peerings')[copyIndex()], 'allowVirtualNetworkAccess'), createObject('value', parameters('peerings')[copyIndex()].allowVirtualNetworkAccess), createObject('value', true()))]", - "doNotVerifyRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'doNotVerifyRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].doNotVerifyRemoteGateways), createObject('value', true()))]", - "useRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'useRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].useRemoteGateways), createObject('value', false()))]" + "useRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'useRemoteGateways')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -14178,8 +14833,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "39994426069187924" + "version": "0.29.47.4906", + "templateHash": "5206620163504251868" }, "name": "Virtual Network Peerings", "description": "This module deploys a Virtual Network Peering.", @@ -14188,9 +14843,9 @@ "parameters": { "name": { "type": "string", - "defaultValue": "[format('{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkId'), '/')))]", + "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]", "metadata": { - "description": "Optional. The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName." } }, "localVnetName": { @@ -14199,7 +14854,7 @@ "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." } }, - "remoteVirtualNetworkId": { + "remoteVirtualNetworkResourceId": { "type": "string", "metadata": { "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." @@ -14244,7 +14899,7 @@ "resources": [ { "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", "properties": { "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", @@ -14253,7 +14908,7 @@ "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", "useRemoteGateways": "[parameters('useRemoteGateways')]", "remoteVirtualNetwork": { - "id": "[parameters('remoteVirtualNetworkId')]" + "id": "[parameters('remoteVirtualNetworkResourceId')]" } } } @@ -14290,14 +14945,14 @@ "virtualNetwork_peering_remote": { "copy": { "name": "virtualNetwork_peering_remote", - "count": "[length(parameters('peerings'))]" + "count": "[length(coalesce(parameters('peerings'), createArray()))]" }, - "condition": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringEnabled'), equals(parameters('peerings')[copyIndex()].remotePeeringEnabled, true()), false())]", + "condition": "[coalesce(tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringEnabled'), false())]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('{0}-virtualNetworkPeering-remote-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", - "subscriptionId": "[split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')[2]]", - "resourceGroup": "[split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')[4]]", + "subscriptionId": "[split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/')[2]]", + "resourceGroup": "[split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -14305,17 +14960,29 @@ "mode": "Incremental", "parameters": { "localVnetName": { - "value": "[last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/'))]" + "value": "[last(split(coalesce(parameters('peerings'), createArray())[copyIndex()].remoteVirtualNetworkResourceId, '/'))]" }, - "remoteVirtualNetworkId": { + "remoteVirtualNetworkResourceId": { "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" }, - "name": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringName'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringName), createObject('value', format('{0}-{1}', last(split(parameters('peerings')[copyIndex()].remoteVirtualNetworkId, '/')), parameters('name'))))]", - "allowForwardedTraffic": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowForwardedTraffic'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowForwardedTraffic), createObject('value', true()))]", - "allowGatewayTransit": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowGatewayTransit'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowGatewayTransit), createObject('value', false()))]", - "allowVirtualNetworkAccess": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringAllowVirtualNetworkAccess'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringAllowVirtualNetworkAccess), createObject('value', true()))]", - "doNotVerifyRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringDoNotVerifyRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringDoNotVerifyRemoteGateways), createObject('value', true()))]", - "useRemoteGateways": "[if(contains(parameters('peerings')[copyIndex()], 'remotePeeringUseRemoteGateways'), createObject('value', parameters('peerings')[copyIndex()].remotePeeringUseRemoteGateways), createObject('value', false()))]" + "name": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringName')]" + }, + "allowForwardedTraffic": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowForwardedTraffic')]" + }, + "allowGatewayTransit": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowGatewayTransit')]" + }, + "allowVirtualNetworkAccess": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringAllowVirtualNetworkAccess')]" + }, + "doNotVerifyRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringDoNotVerifyRemoteGateways')]" + }, + "useRemoteGateways": { + "value": "[tryGet(coalesce(parameters('peerings'), createArray())[copyIndex()], 'remotePeeringUseRemoteGateways')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -14323,8 +14990,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.28.1.47646", - "templateHash": "39994426069187924" + "version": "0.29.47.4906", + "templateHash": "5206620163504251868" }, "name": "Virtual Network Peerings", "description": "This module deploys a Virtual Network Peering.", @@ -14333,9 +15000,9 @@ "parameters": { "name": { "type": "string", - "defaultValue": "[format('{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkId'), '/')))]", + "defaultValue": "[format('peer-{0}-{1}', parameters('localVnetName'), last(split(parameters('remoteVirtualNetworkResourceId'), '/')))]", "metadata": { - "description": "Optional. The Name of Vnet Peering resource. If not provided, default value will be localVnetName-remoteVnetName." + "description": "Optional. The Name of VNET Peering resource. If not provided, default value will be localVnetName-remoteVnetName." } }, "localVnetName": { @@ -14344,7 +15011,7 @@ "description": "Conditional. The name of the parent Virtual Network to add the peering to. Required if the template is used in a standalone deployment." } }, - "remoteVirtualNetworkId": { + "remoteVirtualNetworkResourceId": { "type": "string", "metadata": { "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." @@ -14389,7 +15056,7 @@ "resources": [ { "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", - "apiVersion": "2023-11-01", + "apiVersion": "2024-01-01", "name": "[format('{0}/{1}', parameters('localVnetName'), parameters('name'))]", "properties": { "allowForwardedTraffic": "[parameters('allowForwardedTraffic')]", @@ -14398,7 +15065,7 @@ "doNotVerifyRemoteGateways": "[parameters('doNotVerifyRemoteGateways')]", "useRemoteGateways": "[parameters('useRemoteGateways')]", "remoteVirtualNetwork": { - "id": "[parameters('remoteVirtualNetworkId')]" + "id": "[parameters('remoteVirtualNetworkResourceId')]" } } } @@ -14461,8 +15128,8 @@ "description": "The names of the deployed subnets." }, "copy": { - "count": "[length(parameters('subnets'))]", - "input": "[parameters('subnets')[copyIndex()].name]" + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "input": "[reference(format('virtualNetwork_subnets[{0}]', copyIndex())).outputs.name.value]" } }, "subnetResourceIds": { @@ -14471,8 +15138,8 @@ "description": "The resource IDs of the deployed subnets." }, "copy": { - "count": "[length(parameters('subnets'))]", - "input": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('name'), parameters('subnets')[copyIndex()].name)]" + "count": "[length(coalesce(parameters('subnets'), createArray()))]", + "input": "[reference(format('virtualNetwork_subnets[{0}]', copyIndex())).outputs.resourceId.value]" } }, "location": { @@ -14480,7 +15147,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('virtualNetwork', '2023-11-01', 'full').location]" + "value": "[reference('virtualNetwork', '2024-01-01', 'full').location]" } } } diff --git a/avm/ptn/lz/sub-vending/modules/subResourceWrapper.bicep b/avm/ptn/lz/sub-vending/modules/subResourceWrapper.bicep index bd8fddf190..ab9684a5a6 100644 --- a/avm/ptn/lz/sub-vending/modules/subResourceWrapper.bicep +++ b/avm/ptn/lz/sub-vending/modules/subResourceWrapper.bicep @@ -361,7 +361,7 @@ module tagResourceGroup 'tags.bicep' = if (virtualNetworkEnabled && !empty(virtu } } -module createLzVnet 'br/public:avm/res/network/virtual-network:0.1.7' = if (virtualNetworkEnabled && !empty(virtualNetworkName) && !empty(virtualNetworkAddressSpace) && !empty(virtualNetworkLocation) && !empty(virtualNetworkResourceGroupName)) { +module createLzVnet 'br/public:avm/res/network/virtual-network:0.4.0' = if (virtualNetworkEnabled && !empty(virtualNetworkName) && !empty(virtualNetworkAddressSpace) && !empty(virtualNetworkLocation) && !empty(virtualNetworkResourceGroupName)) { dependsOn: [ createResourceGroupForLzNetworking ] @@ -377,19 +377,19 @@ module createLzVnet 'br/public:avm/res/network/virtual-network:0.1.7' = if (virt peerings: (virtualNetworkEnabled && virtualNetworkPeeringEnabled && !empty(hubVirtualNetworkResourceIdChecked) && !empty(virtualNetworkName) && !empty(virtualNetworkAddressSpace) && !empty(virtualNetworkLocation) && !empty(virtualNetworkResourceGroupName)) ? [ { + remoteVirtualNetworkResourceId: hubVirtualNetworkResourceIdChecked allowForwardedTraffic: true allowVirtualNetworkAccess: true allowGatewayTransit: false useRemoteGateways: virtualNetworkUseRemoteGateways remotePeeringEnabled: virtualNetworkPeeringEnabled - remoteVirtualNetworkId: hubVirtualNetworkResourceIdChecked remotePeeringAllowForwardedTraffic: true remotePeeringAllowVirtualNetworkAccess: true remotePeeringAllowGatewayTransit: true remotePeeringUseRemoteGateways: false } ] - : [] + : null enableTelemetry: enableTelemetry } } @@ -552,7 +552,7 @@ module createDsStorageAccount 'br/public:avm/res/storage/storage-account:0.9.1' } } -module createDsVnet 'br/public:avm/res/network/virtual-network:0.1.7' = if (!empty(resourceProviders)) { +module createDsVnet 'br/public:avm/res/network/virtual-network:0.4.0' = if (!empty(resourceProviders)) { scope: resourceGroup(subscriptionId, deploymentScriptResourceGroupName) name: deploymentNames.createdsVnet params: { @@ -561,26 +561,21 @@ module createDsVnet 'br/public:avm/res/network/virtual-network:0.1.7' = if (!emp addressPrefixes: [ virtualNetworkDeploymentScriptAddressPrefix ] - subnets: [ - { - addressPrefix: !empty(resourceProviders) ? cidrSubnet(virtualNetworkDeploymentScriptAddressPrefix, 24, 0) : null - name: 'ds-subnet-001' - networkSecurityGroupResourceId: !empty(resourceProviders) ? createDsNsg.outputs.resourceId : null - serviceEndpoints: [ - { - service: 'Microsoft.Storage' - } - ] - delegations: [ + subnets: !empty(resourceProviders) + ? [ { - name: 'Microsoft.ContainerInstance.containerGroups' - properties: { - serviceName: 'Microsoft.ContainerInstance/containerGroups' - } + addressPrefix: !empty(resourceProviders) + ? cidrSubnet(virtualNetworkDeploymentScriptAddressPrefix, 24, 0) + : null + name: 'ds-subnet-001' + networkSecurityGroupResourceId: !empty(resourceProviders) ? createDsNsg.outputs.resourceId : null + serviceEndpoints: [ + 'Microsoft.Storage' + ] + delegation: 'Microsoft.ContainerInstance/containerGroups' } ] - } - ] + : null enableTelemetry: enableTelemetry } } From 2a435324edf8b63919af3c97b20eea38f14ef218 Mon Sep 17 00:00:00 2001 From: Tony Box Date: Mon, 30 Sep 2024 14:35:24 -0700 Subject: [PATCH 49/57] fix: add displayName to more submodules (#3342) ## Description Fixes #3328 - Add option to set displayName for authorizationserver, product, subscription resources ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.api-management.service](https://github.com/tony-box/bicep-registry-modules/actions/workflows/avm.res.api-management.service.yml/badge.svg?branch=feat%2Fapim_apiupdate)](https://github.com/tony-box/bicep-registry-modules/actions/workflows/avm.res.api-management.service.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Tony Box --- avm/res/api-management/service/README.md | 12 ++ .../service/api-version-set/main.json | 4 +- .../service/api/diagnostics/main.json | 4 +- avm/res/api-management/service/api/main.json | 12 +- .../service/api/policy/main.json | 4 +- .../service/authorization-server/README.md | 8 ++ .../service/authorization-server/main.bicep | 6 +- .../service/authorization-server/main.json | 13 ++- .../api-management/service/backend/main.json | 4 +- .../api-management/service/cache/main.json | 4 +- .../service/identity-provider/main.json | 4 +- .../api-management/service/loggers/main.json | 4 +- avm/res/api-management/service/main.bicep | 3 + avm/res/api-management/service/main.json | 108 +++++++++++------- .../service/named-value/main.json | 4 +- .../api-management/service/policy/main.json | 4 +- .../service/portalsetting/main.json | 4 +- .../api-management/service/product/README.md | 8 ++ .../service/product/api/main.json | 4 +- .../service/product/group/main.json | 4 +- .../api-management/service/product/main.bicep | 6 +- .../api-management/service/product/main.json | 21 ++-- .../service/subscription/README.md | 8 ++ .../service/subscription/main.bicep | 6 +- .../service/subscription/main.json | 13 ++- .../service/tests/e2e/max/main.test.bicep | 3 + .../tests/e2e/waf-aligned/main.test.bicep | 2 + avm/res/api-management/service/version.json | 4 +- 28 files changed, 194 insertions(+), 87 deletions(-) diff --git a/avm/res/api-management/service/README.md b/avm/res/api-management/service/README.md index c219b2de19..b95709dc50 100644 --- a/avm/res/api-management/service/README.md +++ b/avm/res/api-management/service/README.md @@ -290,6 +290,7 @@ module service 'br/public:avm/res/api-management/service:' = { clientId: 'apimclientid' clientRegistrationEndpoint: 'http://localhost' clientSecret: '' + displayName: 'AuthServer1' grantTypes: [ 'authorizationCode' ] @@ -404,6 +405,7 @@ module service 'br/public:avm/res/api-management/service:' = { } ] approvalRequired: false + displayName: 'Starter' groups: [ { name: 'developers' @@ -436,6 +438,7 @@ module service 'br/public:avm/res/api-management/service:' = { subnetResourceId: '' subscriptions: [ { + displayName: 'testArmSubscriptionAllApis' name: 'testArmSubscriptionAllApis' scope: '/apis' } @@ -525,6 +528,7 @@ module service 'br/public:avm/res/api-management/service:' = { "clientId": "apimclientid", "clientRegistrationEndpoint": "http://localhost", "clientSecret": "", + "displayName": "AuthServer1", "grantTypes": [ "authorizationCode" ], @@ -663,6 +667,7 @@ module service 'br/public:avm/res/api-management/service:' = { } ], "approvalRequired": false, + "displayName": "Starter", "groups": [ { "name": "developers" @@ -703,6 +708,7 @@ module service 'br/public:avm/res/api-management/service:' = { "subscriptions": { "value": [ { + "displayName": "testArmSubscriptionAllApis", "name": "testArmSubscriptionAllApis", "scope": "/apis" } @@ -837,6 +843,7 @@ module service 'br/public:avm/res/api-management/service:' = { clientId: 'apimClientid' clientRegistrationEndpoint: 'https://localhost' clientSecret: '' + displayName: 'AuthServer1' grantTypes: [ 'authorizationCode' ] @@ -973,6 +980,7 @@ module service 'br/public:avm/res/api-management/service:' = { ] subscriptions: [ { + displayName: 'testArmSubscriptionAllApis' name: 'testArmSubscriptionAllApis' scope: '/apis' } @@ -1048,6 +1056,7 @@ module service 'br/public:avm/res/api-management/service:' = { "clientId": "apimClientid", "clientRegistrationEndpoint": "https://localhost", "clientSecret": "", + "displayName": "AuthServer1", "grantTypes": [ "authorizationCode" ], @@ -1212,6 +1221,7 @@ module service 'br/public:avm/res/api-management/service:' = { "subscriptions": { "value": [ { + "displayName": "testArmSubscriptionAllApis", "name": "testArmSubscriptionAllApis", "scope": "/apis" } @@ -1927,6 +1937,8 @@ A list of availability zones denoting where the resource needs to come from. Onl ## Notes +The latest version of this module only includes supported versions of the API Management resource. All unsupported versions of API Management have been removed from the related parameters. See the [API Management stv1 platform retirement](!https://learn.microsoft.com/en-us/azure/api-management/breaking-changes/stv1-platform-retirement-august-2024) article for more details. + ### Parameter Usage: `apiManagementServicePolicy`
diff --git a/avm/res/api-management/service/api-version-set/main.json b/avm/res/api-management/service/api-version-set/main.json index 85639acf5c..061641030c 100644 --- a/avm/res/api-management/service/api-version-set/main.json +++ b/avm/res/api-management/service/api-version-set/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "17159723717884761443" + "version": "0.30.23.60470", + "templateHash": "2492486199367242598" }, "name": "API Management Service API Version Sets", "description": "This module deploys an API Management Service API Version Set.", diff --git a/avm/res/api-management/service/api/diagnostics/main.json b/avm/res/api-management/service/api/diagnostics/main.json index 83e2b3a003..6db7e0f400 100644 --- a/avm/res/api-management/service/api/diagnostics/main.json +++ b/avm/res/api-management/service/api/diagnostics/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15630166564208731013" + "version": "0.30.23.60470", + "templateHash": "2531959928497745895" }, "name": "API Management Service APIs Diagnostics.", "description": "This module deploys an API Management Service API Diagnostics.", diff --git a/avm/res/api-management/service/api/main.json b/avm/res/api-management/service/api/main.json index 970b83350d..a87b3409db 100644 --- a/avm/res/api-management/service/api/main.json +++ b/avm/res/api-management/service/api/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "17160750790361326516" + "version": "0.30.23.60470", + "templateHash": "17036957862982683599" }, "name": "API Management Service APIs", "description": "This module deploys an API Management Service API.", @@ -283,8 +283,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "2474188503939052987" + "version": "0.30.23.60470", + "templateHash": "5643177447182050438" }, "name": "API Management Service APIs Policies", "description": "This module deploys an API Management Service API Policy.", @@ -430,8 +430,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15630166564208731013" + "version": "0.30.23.60470", + "templateHash": "2531959928497745895" }, "name": "API Management Service APIs Diagnostics.", "description": "This module deploys an API Management Service API Diagnostics.", diff --git a/avm/res/api-management/service/api/policy/main.json b/avm/res/api-management/service/api/policy/main.json index 6defcce4a3..af5ae11307 100644 --- a/avm/res/api-management/service/api/policy/main.json +++ b/avm/res/api-management/service/api/policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "2474188503939052987" + "version": "0.30.23.60470", + "templateHash": "5643177447182050438" }, "name": "API Management Service APIs Policies", "description": "This module deploys an API Management Service API Policy.", diff --git a/avm/res/api-management/service/authorization-server/README.md b/avm/res/api-management/service/authorization-server/README.md index d6996e57a1..234576e93d 100644 --- a/avm/res/api-management/service/authorization-server/README.md +++ b/avm/res/api-management/service/authorization-server/README.md @@ -23,6 +23,7 @@ This module deploys an API Management Service Authorization Server. | [`authorizationEndpoint`](#parameter-authorizationendpoint) | string | OAuth authorization endpoint. See . | | [`clientId`](#parameter-clientid) | securestring | Client or app ID registered with this authorization server. | | [`clientSecret`](#parameter-clientsecret) | securestring | Client or app secret registered with this authorization server. This property will not be filled on 'GET' operations! Use '/listSecrets' POST request to get the value. | +| [`displayName`](#parameter-displayname) | string | API Management Service Authorization Servers name. Must be 1 to 50 characters long. | | [`grantTypes`](#parameter-granttypes) | array | Form of an authorization grant, which the client uses to request the access token. - authorizationCode, implicit, resourceOwnerPassword, clientCredentials. | | [`name`](#parameter-name) | string | Identifier of the authorization server. | @@ -69,6 +70,13 @@ Client or app secret registered with this authorization server. This property wi - Required: Yes - Type: securestring +### Parameter: `displayName` + +API Management Service Authorization Servers name. Must be 1 to 50 characters long. + +- Required: Yes +- Type: string + ### Parameter: `grantTypes` Form of an authorization grant, which the client uses to request the access token. - authorizationCode, implicit, resourceOwnerPassword, clientCredentials. diff --git a/avm/res/api-management/service/authorization-server/main.bicep b/avm/res/api-management/service/authorization-server/main.bicep index 3be6ae0b89..afe57ae5cd 100644 --- a/avm/res/api-management/service/authorization-server/main.bicep +++ b/avm/res/api-management/service/authorization-server/main.bicep @@ -5,6 +5,10 @@ metadata owner = 'Azure/module-maintainers' @description('Required. Identifier of the authorization server.') param name string +@description('Required. API Management Service Authorization Servers name. Must be 1 to 50 characters long.') +@maxLength(50) +param displayName string + @description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') param apiManagementServiceName string @@ -85,7 +89,7 @@ resource authorizationServer 'Microsoft.ApiManagement/service/authorizationServe bearerTokenSendingMethods: bearerTokenSendingMethods resourceOwnerUsername: resourceOwnerUsername resourceOwnerPassword: resourceOwnerPassword - displayName: name + displayName: displayName clientRegistrationEndpoint: clientRegistrationEndpoint authorizationEndpoint: authorizationEndpoint grantTypes: grantTypes diff --git a/avm/res/api-management/service/authorization-server/main.json b/avm/res/api-management/service/authorization-server/main.json index e966a03d7f..50d0897a93 100644 --- a/avm/res/api-management/service/authorization-server/main.json +++ b/avm/res/api-management/service/authorization-server/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "4256977187793378377" + "version": "0.30.23.60470", + "templateHash": "17927787726774417819" }, "name": "API Management Service Authorization Servers", "description": "This module deploys an API Management Service Authorization Server.", @@ -18,6 +18,13 @@ "description": "Required. Identifier of the authorization server." } }, + "displayName": { + "type": "string", + "maxLength": 50, + "metadata": { + "description": "Required. API Management Service Authorization Servers name. Must be 1 to 50 characters long." + } + }, "apiManagementServiceName": { "type": "string", "metadata": { @@ -154,7 +161,7 @@ "bearerTokenSendingMethods": "[parameters('bearerTokenSendingMethods')]", "resourceOwnerUsername": "[parameters('resourceOwnerUsername')]", "resourceOwnerPassword": "[parameters('resourceOwnerPassword')]", - "displayName": "[parameters('name')]", + "displayName": "[parameters('displayName')]", "clientRegistrationEndpoint": "[parameters('clientRegistrationEndpoint')]", "authorizationEndpoint": "[parameters('authorizationEndpoint')]", "grantTypes": "[parameters('grantTypes')]", diff --git a/avm/res/api-management/service/backend/main.json b/avm/res/api-management/service/backend/main.json index 2a5ea70d52..c3ae5f49b2 100644 --- a/avm/res/api-management/service/backend/main.json +++ b/avm/res/api-management/service/backend/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "2365531440872951056" + "version": "0.30.23.60470", + "templateHash": "14706757128951530017" }, "name": "API Management Service Backends", "description": "This module deploys an API Management Service Backend.", diff --git a/avm/res/api-management/service/cache/main.json b/avm/res/api-management/service/cache/main.json index b66a377833..285f53b0fb 100644 --- a/avm/res/api-management/service/cache/main.json +++ b/avm/res/api-management/service/cache/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3234729148013684780" + "version": "0.30.23.60470", + "templateHash": "2750555671183513052" }, "name": "API Management Service Caches", "description": "This module deploys an API Management Service Cache.", diff --git a/avm/res/api-management/service/identity-provider/main.json b/avm/res/api-management/service/identity-provider/main.json index f9e6cbe086..6768ba8a3e 100644 --- a/avm/res/api-management/service/identity-provider/main.json +++ b/avm/res/api-management/service/identity-provider/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "12757169124799431378" + "version": "0.30.23.60470", + "templateHash": "1342690797398622979" }, "name": "API Management Service Identity Providers", "description": "This module deploys an API Management Service Identity Provider.", diff --git a/avm/res/api-management/service/loggers/main.json b/avm/res/api-management/service/loggers/main.json index 9a6b6378bd..7d3305a3cd 100644 --- a/avm/res/api-management/service/loggers/main.json +++ b/avm/res/api-management/service/loggers/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "12986610229102962453" + "version": "0.30.23.60470", + "templateHash": "12834599511984803283" }, "name": "API Management Service Loggers", "description": "This module deploys an API Management Service Logger.", diff --git a/avm/res/api-management/service/main.bicep b/avm/res/api-management/service/main.bicep index 1e69199417..37a49b86c0 100644 --- a/avm/res/api-management/service/main.bicep +++ b/avm/res/api-management/service/main.bicep @@ -310,6 +310,7 @@ module service_authorizationServers 'authorization-server/main.bicep' = [ params: { apiManagementServiceName: service.name name: authorizationServer.name + displayName: authorizationServer.displayName authorizationEndpoint: authorizationServer.authorizationEndpoint authorizationMethods: authorizationServer.?authorizationMethods ?? ['GET'] bearerTokenSendingMethods: authorizationServer.?bearerTokenSendingMethods ?? ['authorizationHeader'] @@ -467,6 +468,7 @@ module service_products 'product/main.bicep' = [ for (product, index) in products: { name: '${uniqueString(deployment().name, location)}-Apim-Product-${index}' params: { + displayName: product.displayName apiManagementServiceName: service.name apis: product.?apis ?? [] approvalRequired: product.?approvalRequired ?? false @@ -490,6 +492,7 @@ module service_subscriptions 'subscription/main.bicep' = [ params: { apiManagementServiceName: service.name name: subscription.name + displayName: subscription.displayName allowTracing: subscription.?allowTracing ownerId: subscription.?ownerId primaryKey: subscription.?primaryKey diff --git a/avm/res/api-management/service/main.json b/avm/res/api-management/service/main.json index 64675ab023..fc42a71966 100644 --- a/avm/res/api-management/service/main.json +++ b/avm/res/api-management/service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "5150103142771299599" + "version": "0.30.23.60470", + "templateHash": "7676062632439815762" }, "name": "API Management Services", "description": "This module deploys an API Management Service. The default deployment is set to use a Premium SKU to align with Microsoft WAF-aligned best practices. In most cases, non-prod deployments should use a lower-tier SKU.", @@ -791,8 +791,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "17160750790361326516" + "version": "0.30.23.60470", + "templateHash": "17036957862982683599" }, "name": "API Management Service APIs", "description": "This module deploys an API Management Service API.", @@ -1069,8 +1069,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "2474188503939052987" + "version": "0.30.23.60470", + "templateHash": "5643177447182050438" }, "name": "API Management Service APIs Policies", "description": "This module deploys an API Management Service API Policy.", @@ -1216,8 +1216,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15630166564208731013" + "version": "0.30.23.60470", + "templateHash": "2531959928497745895" }, "name": "API Management Service APIs Diagnostics.", "description": "This module deploys an API Management Service API Diagnostics.", @@ -1444,8 +1444,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "17159723717884761443" + "version": "0.30.23.60470", + "templateHash": "2492486199367242598" }, "name": "API Management Service API Version Sets", "description": "This module deploys an API Management Service API Version Set.", @@ -1530,6 +1530,9 @@ "name": { "value": "[variables('authorizationServerList')[copyIndex()].name]" }, + "displayName": { + "value": "[variables('authorizationServerList')[copyIndex()].displayName]" + }, "authorizationEndpoint": { "value": "[variables('authorizationServerList')[copyIndex()].authorizationEndpoint]" }, @@ -1582,8 +1585,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "4256977187793378377" + "version": "0.30.23.60470", + "templateHash": "17927787726774417819" }, "name": "API Management Service Authorization Servers", "description": "This module deploys an API Management Service Authorization Server.", @@ -1596,6 +1599,13 @@ "description": "Required. Identifier of the authorization server." } }, + "displayName": { + "type": "string", + "maxLength": 50, + "metadata": { + "description": "Required. API Management Service Authorization Servers name. Must be 1 to 50 characters long." + } + }, "apiManagementServiceName": { "type": "string", "metadata": { @@ -1732,7 +1742,7 @@ "bearerTokenSendingMethods": "[parameters('bearerTokenSendingMethods')]", "resourceOwnerUsername": "[parameters('resourceOwnerUsername')]", "resourceOwnerPassword": "[parameters('resourceOwnerPassword')]", - "displayName": "[parameters('name')]", + "displayName": "[parameters('displayName')]", "clientRegistrationEndpoint": "[parameters('clientRegistrationEndpoint')]", "authorizationEndpoint": "[parameters('authorizationEndpoint')]", "grantTypes": "[parameters('grantTypes')]", @@ -1825,8 +1835,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "2365531440872951056" + "version": "0.30.23.60470", + "templateHash": "14706757128951530017" }, "name": "API Management Service Backends", "description": "This module deploys an API Management Service Backend.", @@ -2009,8 +2019,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3234729148013684780" + "version": "0.30.23.60470", + "templateHash": "2750555671183513052" }, "name": "API Management Service Caches", "description": "This module deploys an API Management Service Cache.", @@ -2167,8 +2177,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15630166564208731013" + "version": "0.30.23.60470", + "templateHash": "2531959928497745895" }, "name": "API Management Service APIs Diagnostics.", "description": "This module deploys an API Management Service API Diagnostics.", @@ -2397,8 +2407,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "12757169124799431378" + "version": "0.30.23.60470", + "templateHash": "1342690797398622979" }, "name": "API Management Service Identity Providers", "description": "This module deploys an API Management Service Identity Provider.", @@ -2610,8 +2620,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "12986610229102962453" + "version": "0.30.23.60470", + "templateHash": "12834599511984803283" }, "name": "API Management Service Loggers", "description": "This module deploys an API Management Service Logger.", @@ -2754,8 +2764,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3479776319506170502" + "version": "0.30.23.60470", + "templateHash": "10162843567606353040" }, "name": "API Management Service Named Values", "description": "This module deploys an API Management Service Named Value.", @@ -2895,8 +2905,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "10271256088614129674" + "version": "0.30.23.60470", + "templateHash": "14869704072680236257" }, "name": "API Management Service Portal Settings", "description": "This module deploys an API Management Service Portal Setting.", @@ -2994,8 +3004,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11443463088593763324" + "version": "0.30.23.60470", + "templateHash": "9395795206748286282" }, "name": "API Management Service Policies", "description": "This module deploys an API Management Service Policy.", @@ -3089,6 +3099,9 @@ }, "mode": "Incremental", "parameters": { + "displayName": { + "value": "[parameters('products')[copyIndex()].displayName]" + }, "apiManagementServiceName": { "value": "[parameters('name')]" }, @@ -3126,8 +3139,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "6230115773857876317" + "version": "0.30.23.60470", + "templateHash": "8029364311033748838" }, "name": "API Management Service Products", "description": "This module deploys an API Management Service Product.", @@ -3140,6 +3153,13 @@ "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." } }, + "displayName": { + "type": "string", + "maxLength": 300, + "metadata": { + "description": "Required. API Management Service Products name. Must be 1 to 300 characters long." + } + }, "approvalRequired": { "type": "bool", "defaultValue": false, @@ -3210,7 +3230,7 @@ "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", "properties": { "description": "[parameters('description')]", - "displayName": "[parameters('name')]", + "displayName": "[parameters('displayName')]", "terms": "[parameters('terms')]", "subscriptionRequired": "[parameters('subscriptionRequired')]", "approvalRequired": "[if(parameters('subscriptionRequired'), parameters('approvalRequired'), null())]", @@ -3248,8 +3268,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1052981479169082206" + "version": "0.30.23.60470", + "templateHash": "602104798329438871" }, "name": "API Management Service Products APIs", "description": "This module deploys an API Management Service Product API.", @@ -3338,8 +3358,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "5748451278124986706" + "version": "0.30.23.60470", + "templateHash": "5238408376918932137" }, "name": "API Management Service Products Groups", "description": "This module deploys an API Management Service Product Group.", @@ -3469,6 +3489,9 @@ "name": { "value": "[parameters('subscriptions')[copyIndex()].name]" }, + "displayName": { + "value": "[parameters('subscriptions')[copyIndex()].displayName]" + }, "allowTracing": { "value": "[tryGet(parameters('subscriptions')[copyIndex()], 'allowTracing')]" }, @@ -3495,8 +3518,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "9499976066778278010" + "version": "0.30.23.60470", + "templateHash": "16082435269276611452" }, "name": "API Management Service Subscriptions", "description": "This module deploys an API Management Service Subscription.", @@ -3510,6 +3533,13 @@ "description": "Optional. Determines whether tracing can be enabled." } }, + "displayName": { + "type": "string", + "maxLength": 100, + "metadata": { + "description": "Required. API Management Service Subscriptions name. Must be 1 to 100 characters long." + } + }, "apiManagementServiceName": { "type": "string", "metadata": { @@ -3571,7 +3601,7 @@ "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", "properties": { "scope": "[parameters('scope')]", - "displayName": "[parameters('name')]", + "displayName": "[parameters('displayName')]", "ownerId": "[parameters('ownerId')]", "primaryKey": "[parameters('primaryKey')]", "secondaryKey": "[parameters('secondaryKey')]", diff --git a/avm/res/api-management/service/named-value/main.json b/avm/res/api-management/service/named-value/main.json index 4be9cba518..b182535671 100644 --- a/avm/res/api-management/service/named-value/main.json +++ b/avm/res/api-management/service/named-value/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3479776319506170502" + "version": "0.30.23.60470", + "templateHash": "10162843567606353040" }, "name": "API Management Service Named Values", "description": "This module deploys an API Management Service Named Value.", diff --git a/avm/res/api-management/service/policy/main.json b/avm/res/api-management/service/policy/main.json index 83d9434240..dd3c7eab82 100644 --- a/avm/res/api-management/service/policy/main.json +++ b/avm/res/api-management/service/policy/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11443463088593763324" + "version": "0.30.23.60470", + "templateHash": "9395795206748286282" }, "name": "API Management Service Policies", "description": "This module deploys an API Management Service Policy.", diff --git a/avm/res/api-management/service/portalsetting/main.json b/avm/res/api-management/service/portalsetting/main.json index 779c574120..d68c8ed791 100644 --- a/avm/res/api-management/service/portalsetting/main.json +++ b/avm/res/api-management/service/portalsetting/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "10271256088614129674" + "version": "0.30.23.60470", + "templateHash": "14869704072680236257" }, "name": "API Management Service Portal Settings", "description": "This module deploys an API Management Service Portal Setting.", diff --git a/avm/res/api-management/service/product/README.md b/avm/res/api-management/service/product/README.md index c5b8331a4a..9dbc604abb 100644 --- a/avm/res/api-management/service/product/README.md +++ b/avm/res/api-management/service/product/README.md @@ -22,6 +22,7 @@ This module deploys an API Management Service Product. | Parameter | Type | Description | | :-- | :-- | :-- | +| [`displayName`](#parameter-displayname) | string | API Management Service Products name. Must be 1 to 300 characters long. | | [`name`](#parameter-name) | string | Product Name. | **Conditional parameters** @@ -43,6 +44,13 @@ This module deploys an API Management Service Product. | [`subscriptionsLimit`](#parameter-subscriptionslimit) | int | Whether the number of subscriptions a user can have to this product at the same time. Set to null or omit to allow unlimited per user subscriptions. Can be present only if subscriptionRequired property is present and has a value of false. | | [`terms`](#parameter-terms) | string | Product terms of use. Developers trying to subscribe to the product will be presented and required to accept these terms before they can complete the subscription process. | +### Parameter: `displayName` + +API Management Service Products name. Must be 1 to 300 characters long. + +- Required: Yes +- Type: string + ### Parameter: `name` Product Name. diff --git a/avm/res/api-management/service/product/api/main.json b/avm/res/api-management/service/product/api/main.json index 4042b9bf61..5603f9f789 100644 --- a/avm/res/api-management/service/product/api/main.json +++ b/avm/res/api-management/service/product/api/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1052981479169082206" + "version": "0.30.23.60470", + "templateHash": "602104798329438871" }, "name": "API Management Service Products APIs", "description": "This module deploys an API Management Service Product API.", diff --git a/avm/res/api-management/service/product/group/main.json b/avm/res/api-management/service/product/group/main.json index 4ac13f0dac..28d5460152 100644 --- a/avm/res/api-management/service/product/group/main.json +++ b/avm/res/api-management/service/product/group/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "5748451278124986706" + "version": "0.30.23.60470", + "templateHash": "5238408376918932137" }, "name": "API Management Service Products Groups", "description": "This module deploys an API Management Service Product Group.", diff --git a/avm/res/api-management/service/product/main.bicep b/avm/res/api-management/service/product/main.bicep index 9787974281..22ca0081c1 100644 --- a/avm/res/api-management/service/product/main.bicep +++ b/avm/res/api-management/service/product/main.bicep @@ -5,6 +5,10 @@ metadata owner = 'Azure/module-maintainers' @sys.description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') param apiManagementServiceName string +@sys.description('Required. API Management Service Products name. Must be 1 to 300 characters long.') +@maxLength(300) +param displayName string + @sys.description('Optional. Whether subscription approval is required. If false, new subscriptions will be approved automatically enabling developers to call the products APIs immediately after subscribing. If true, administrators must manually approve the subscription before the developer can any of the products APIs. Can be present only if subscriptionRequired property is present and has a value of false.') param approvalRequired bool = false @@ -41,7 +45,7 @@ resource product 'Microsoft.ApiManagement/service/products@2022-08-01' = { parent: service properties: { description: description - displayName: name + displayName: displayName terms: terms subscriptionRequired: subscriptionRequired approvalRequired: subscriptionRequired ? approvalRequired : null diff --git a/avm/res/api-management/service/product/main.json b/avm/res/api-management/service/product/main.json index 73dd3977b6..892a25de5c 100644 --- a/avm/res/api-management/service/product/main.json +++ b/avm/res/api-management/service/product/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "6230115773857876317" + "version": "0.30.23.60470", + "templateHash": "8029364311033748838" }, "name": "API Management Service Products", "description": "This module deploys an API Management Service Product.", @@ -18,6 +18,13 @@ "description": "Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment." } }, + "displayName": { + "type": "string", + "maxLength": 300, + "metadata": { + "description": "Required. API Management Service Products name. Must be 1 to 300 characters long." + } + }, "approvalRequired": { "type": "bool", "defaultValue": false, @@ -88,7 +95,7 @@ "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", "properties": { "description": "[parameters('description')]", - "displayName": "[parameters('name')]", + "displayName": "[parameters('displayName')]", "terms": "[parameters('terms')]", "subscriptionRequired": "[parameters('subscriptionRequired')]", "approvalRequired": "[if(parameters('subscriptionRequired'), parameters('approvalRequired'), null())]", @@ -126,8 +133,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1052981479169082206" + "version": "0.30.23.60470", + "templateHash": "602104798329438871" }, "name": "API Management Service Products APIs", "description": "This module deploys an API Management Service Product API.", @@ -216,8 +223,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "5748451278124986706" + "version": "0.30.23.60470", + "templateHash": "5238408376918932137" }, "name": "API Management Service Products Groups", "description": "This module deploys an API Management Service Product Group.", diff --git a/avm/res/api-management/service/subscription/README.md b/avm/res/api-management/service/subscription/README.md index c851bed0dd..03e97821cd 100644 --- a/avm/res/api-management/service/subscription/README.md +++ b/avm/res/api-management/service/subscription/README.md @@ -20,6 +20,7 @@ This module deploys an API Management Service Subscription. | Parameter | Type | Description | | :-- | :-- | :-- | +| [`displayName`](#parameter-displayname) | string | API Management Service Subscriptions name. Must be 1 to 100 characters long. | | [`name`](#parameter-name) | string | Subscription name. | **Conditional parameters** @@ -39,6 +40,13 @@ This module deploys an API Management Service Subscription. | [`secondaryKey`](#parameter-secondarykey) | string | Secondary subscription key. If not specified during request key will be generated automatically. | | [`state`](#parameter-state) | string | Initial subscription state. If no value is specified, subscription is created with Submitted state. Possible states are "*" active "?" the subscription is active, "*" suspended "?" the subscription is blocked, and the subscriber cannot call any APIs of the product, * submitted ? the subscription request has been made by the developer, but has not yet been approved or rejected, * rejected ? the subscription request has been denied by an administrator, * cancelled ? the subscription has been cancelled by the developer or administrator, * expired ? the subscription reached its expiration date and was deactivated. - suspended, active, expired, submitted, rejected, cancelled. | +### Parameter: `displayName` + +API Management Service Subscriptions name. Must be 1 to 100 characters long. + +- Required: Yes +- Type: string + ### Parameter: `name` Subscription name. diff --git a/avm/res/api-management/service/subscription/main.bicep b/avm/res/api-management/service/subscription/main.bicep index 1b1e9411be..746242a7f9 100644 --- a/avm/res/api-management/service/subscription/main.bicep +++ b/avm/res/api-management/service/subscription/main.bicep @@ -5,6 +5,10 @@ metadata owner = 'Azure/module-maintainers' @description('Optional. Determines whether tracing can be enabled.') param allowTracing bool = true +@description('Required. API Management Service Subscriptions name. Must be 1 to 100 characters long.') +@maxLength(100) +param displayName string + @description('Conditional. The name of the parent API Management service. Required if the template is used in a standalone deployment.') param apiManagementServiceName string @@ -35,7 +39,7 @@ resource subscription 'Microsoft.ApiManagement/service/subscriptions@2022-08-01' parent: service properties: { scope: scope - displayName: name + displayName: displayName ownerId: ownerId primaryKey: primaryKey secondaryKey: secondaryKey diff --git a/avm/res/api-management/service/subscription/main.json b/avm/res/api-management/service/subscription/main.json index 5510d60858..6abc772cc3 100644 --- a/avm/res/api-management/service/subscription/main.json +++ b/avm/res/api-management/service/subscription/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "9499976066778278010" + "version": "0.30.23.60470", + "templateHash": "16082435269276611452" }, "name": "API Management Service Subscriptions", "description": "This module deploys an API Management Service Subscription.", @@ -20,6 +20,13 @@ "description": "Optional. Determines whether tracing can be enabled." } }, + "displayName": { + "type": "string", + "maxLength": 100, + "metadata": { + "description": "Required. API Management Service Subscriptions name. Must be 1 to 100 characters long." + } + }, "apiManagementServiceName": { "type": "string", "metadata": { @@ -81,7 +88,7 @@ "name": "[format('{0}/{1}', parameters('apiManagementServiceName'), parameters('name'))]", "properties": { "scope": "[parameters('scope')]", - "displayName": "[parameters('name')]", + "displayName": "[parameters('displayName')]", "ownerId": "[parameters('ownerId')]", "primaryKey": "[parameters('primaryKey')]", "secondaryKey": "[parameters('secondaryKey')]", diff --git a/avm/res/api-management/service/tests/e2e/max/main.test.bicep b/avm/res/api-management/service/tests/e2e/max/main.test.bicep index 2989495645..e0419365c5 100644 --- a/avm/res/api-management/service/tests/e2e/max/main.test.bicep +++ b/avm/res/api-management/service/tests/e2e/max/main.test.bicep @@ -124,6 +124,7 @@ module testDeployment '../../../main.bicep' = [ 'authorizationCode' ] name: 'AuthServer1' + displayName: 'AuthServer1' tokenEndpoint: '${environment().authentication.loginEndpoint}651b43ce-ccb8-4301-b551-b04dd872d401/oauth2/v2.0/token' } ] @@ -241,6 +242,7 @@ module testDeployment '../../../main.bicep' = [ } ] name: 'Starter' + displayName: 'Starter' subscriptionRequired: false } ] @@ -270,6 +272,7 @@ module testDeployment '../../../main.bicep' = [ { name: 'testArmSubscriptionAllApis' scope: '/apis' + displayName: 'testArmSubscriptionAllApis' } ] managedIdentities: { diff --git a/avm/res/api-management/service/tests/e2e/waf-aligned/main.test.bicep b/avm/res/api-management/service/tests/e2e/waf-aligned/main.test.bicep index ccf1f295b4..b94ad37da7 100644 --- a/avm/res/api-management/service/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/api-management/service/tests/e2e/waf-aligned/main.test.bicep @@ -128,6 +128,7 @@ module testDeployment '../../../main.bicep' = [ 'authorizationCode' ] name: 'AuthServer1' + displayName: 'AuthServer1' tokenEndpoint: '${environment().authentication.loginEndpoint}651b43ce-ccb8-4301-b551-b04dd872d401/oauth2/v2.0/token' } ] @@ -243,6 +244,7 @@ module testDeployment '../../../main.bicep' = [ { name: 'testArmSubscriptionAllApis' scope: '/apis' + displayName: 'testArmSubscriptionAllApis' } ] tags: { diff --git a/avm/res/api-management/service/version.json b/avm/res/api-management/service/version.json index a8eda31021..9ed3662aba 100644 --- a/avm/res/api-management/service/version.json +++ b/avm/res/api-management/service/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} From 3073a74cd75675d7c7ba5ddbcc2450f2482de7c6 Mon Sep 17 00:00:00 2001 From: Anders Eide Date: Tue, 1 Oct 2024 11:12:14 +0200 Subject: [PATCH 50/57] feat: Added cdn profile security policies support - `avm/res/cdn/profile` (#3025) ## Description Adds support for Security Policies on cdn/profile, making it possible to use the module to deploy Azure Front Door Premium with Web Application Firewall. Fixes #2376 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.cdn.profile](https://github.com/anderseide/avm-bicep-registry-modules/actions/workflows/avm.res.cdn.profile.yml/badge.svg?branch=cdn-profile-security-policies)](https://github.com/anderseide/avm-bicep-registry-modules/actions/workflows/avm.res.cdn.profile.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [x] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [x] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Guillaume Beaud <79973892+gbeaud@users.noreply.github.com> --- avm/res/cdn/profile/README.md | 322 +++++++++++++++++- avm/res/cdn/profile/afdEndpoint/main.json | 8 +- .../cdn/profile/afdEndpoint/route/main.json | 4 +- avm/res/cdn/profile/customdomain/main.json | 4 +- avm/res/cdn/profile/endpoint/main.json | 8 +- avm/res/cdn/profile/endpoint/origin/main.json | 4 +- avm/res/cdn/profile/main.bicep | 31 ++ avm/res/cdn/profile/main.json | 278 +++++++++++++-- avm/res/cdn/profile/origingroup/main.json | 8 +- .../cdn/profile/origingroup/origin/main.json | 4 +- avm/res/cdn/profile/ruleset/main.json | 8 +- avm/res/cdn/profile/ruleset/rule/main.json | 4 +- avm/res/cdn/profile/secret/main.json | 4 +- .../cdn/profile/securityPolicies/README.md | 101 ++++++ .../cdn/profile/securityPolicies/main.bicep | 54 +++ .../cdn/profile/securityPolicies/main.json | 128 +++++++ .../tests/e2e/afd.premium/main.test.bicep | 146 ++++++++ avm/res/cdn/profile/version.json | 4 +- 18 files changed, 1060 insertions(+), 60 deletions(-) create mode 100644 avm/res/cdn/profile/securityPolicies/README.md create mode 100644 avm/res/cdn/profile/securityPolicies/main.bicep create mode 100644 avm/res/cdn/profile/securityPolicies/main.json create mode 100644 avm/res/cdn/profile/tests/e2e/afd.premium/main.test.bicep diff --git a/avm/res/cdn/profile/README.md b/avm/res/cdn/profile/README.md index 369cadf61b..e2de8fbea8 100644 --- a/avm/res/cdn/profile/README.md +++ b/avm/res/cdn/profile/README.md @@ -27,6 +27,7 @@ This module deploys a CDN Profile. | `Microsoft.Cdn/profiles/ruleSets` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2023-05-01/profiles/ruleSets) | | `Microsoft.Cdn/profiles/ruleSets/rules` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2023-05-01/profiles/ruleSets/rules) | | `Microsoft.Cdn/profiles/secrets` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/2023-05-01/profiles/secrets) | +| `Microsoft.Cdn/profiles/securityPolicies` | [2024-02-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/profiles/securityPolicies) | ## Usage examples @@ -36,12 +37,245 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br/public:avm/res/cdn/profile:`. -- [As Azure Front Door](#example-1-as-azure-front-door) -- [Using only defaults](#example-2-using-only-defaults) -- [Using large parameter set](#example-3-using-large-parameter-set) -- [WAF-aligned](#example-4-waf-aligned) +- [As Azure Front Door Premium](#example-1-as-azure-front-door-premium) +- [As Azure Front Door](#example-2-as-azure-front-door) +- [Using only defaults](#example-3-using-only-defaults) +- [Using large parameter set](#example-4-using-large-parameter-set) +- [WAF-aligned](#example-5-waf-aligned) -### Example 1: _As Azure Front Door_ +### Example 1: _As Azure Front Door Premium_ + +This instance deploys the module as Azure Front Door Premium. + + +
+ +via Bicep module + +```bicep +module profile 'br/public:avm/res/cdn/profile:' = { + name: 'profileDeployment' + params: { + // Required parameters + name: 'dep-test-cdnpafdp' + sku: 'Premium_AzureFrontDoor' + // Non-required parameters + afdEndpoints: [ + { + name: 'dep-test-cdnpafdp-afd-endpoint' + routes: [ + { + customDomainNames: [ + 'dep-test-cdnpafdp-custom-domain' + ] + name: 'dep-test-cdnpafdp-afd-route' + originGroupName: 'dep-test-cdnpafdp-origin-group' + ruleSets: [ + { + name: 'deptestcdnpafdpruleset' + } + ] + } + ] + } + ] + customDomains: [ + { + certificateType: 'ManagedCertificate' + hostName: 'dep-test-cdnpafdp-custom-domain.azurewebsites.net' + name: 'dep-test-cdnpafdp-custom-domain' + } + ] + location: 'global' + originGroups: [ + { + loadBalancingSettings: { + additionalLatencyInMilliseconds: 50 + sampleSize: 4 + successfulSamplesRequired: 3 + } + name: 'dep-test-cdnpafdp-origin-group' + origins: [ + { + hostName: 'dep-test-cdnpafdp-origin.azurewebsites.net' + name: 'dep-test-cdnpafdp-origin' + } + ] + } + ] + originResponseTimeoutSeconds: 60 + ruleSets: [ + { + name: 'deptestcdnpafdpruleset' + rules: [ + { + actions: [ + { + name: 'UrlRedirect' + parameters: { + customHostname: 'dev-etradefd.trade.azure.defra.cloud' + customPath: '/test123' + destinationProtocol: 'Https' + redirectType: 'PermanentRedirect' + typeName: 'DeliveryRuleUrlRedirectActionParameters' + } + } + ] + name: 'deptestcdnpafdprule' + order: 1 + } + ] + } + ] + securityPolicies: [ + { + associations: [ + { + domains: [ + { + id: '' + } + ] + patternsToMatch: [ + '/*' + ] + } + ] + name: 'deptestcdnpafdpsecpol' + wafPolicyResourceId: '' + } + ] + } +} +``` + +
+

+ +

+ +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": "dep-test-cdnpafdp" + }, + "sku": { + "value": "Premium_AzureFrontDoor" + }, + // Non-required parameters + "afdEndpoints": { + "value": [ + { + "name": "dep-test-cdnpafdp-afd-endpoint", + "routes": [ + { + "customDomainNames": [ + "dep-test-cdnpafdp-custom-domain" + ], + "name": "dep-test-cdnpafdp-afd-route", + "originGroupName": "dep-test-cdnpafdp-origin-group", + "ruleSets": [ + { + "name": "deptestcdnpafdpruleset" + } + ] + } + ] + } + ] + }, + "customDomains": { + "value": [ + { + "certificateType": "ManagedCertificate", + "hostName": "dep-test-cdnpafdp-custom-domain.azurewebsites.net", + "name": "dep-test-cdnpafdp-custom-domain" + } + ] + }, + "location": { + "value": "global" + }, + "originGroups": { + "value": [ + { + "loadBalancingSettings": { + "additionalLatencyInMilliseconds": 50, + "sampleSize": 4, + "successfulSamplesRequired": 3 + }, + "name": "dep-test-cdnpafdp-origin-group", + "origins": [ + { + "hostName": "dep-test-cdnpafdp-origin.azurewebsites.net", + "name": "dep-test-cdnpafdp-origin" + } + ] + } + ] + }, + "originResponseTimeoutSeconds": { + "value": 60 + }, + "ruleSets": { + "value": [ + { + "name": "deptestcdnpafdpruleset", + "rules": [ + { + "actions": [ + { + "name": "UrlRedirect", + "parameters": { + "customHostname": "dev-etradefd.trade.azure.defra.cloud", + "customPath": "/test123", + "destinationProtocol": "Https", + "redirectType": "PermanentRedirect", + "typeName": "DeliveryRuleUrlRedirectActionParameters" + } + } + ], + "name": "deptestcdnpafdprule", + "order": 1 + } + ] + } + ] + }, + "securityPolicies": { + "value": [ + { + "associations": [ + { + "domains": [ + { + "id": "" + } + ], + "patternsToMatch": [ + "/*" + ] + } + ], + "name": "deptestcdnpafdpsecpol", + "wafPolicyResourceId": "" + } + ] + } + } +} +``` + +
+

+ +### Example 2: _As Azure Front Door_ This instance deploys the module as Azure Front Door. @@ -235,7 +469,7 @@ module profile 'br/public:avm/res/cdn/profile:' = {

-### Example 2: _Using only defaults_ +### Example 3: _Using only defaults_ This instance deploys the module with the minimum set of required parameters. @@ -287,7 +521,7 @@ module profile 'br/public:avm/res/cdn/profile:' = {

-### Example 3: _Using large parameter set_ +### Example 4: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -455,7 +689,7 @@ module profile 'br/public:avm/res/cdn/profile:' = {

-### Example 4: _WAF-aligned_ +### Example 5: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -603,6 +837,7 @@ module profile 'br/public:avm/res/cdn/profile:' = { | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | | [`ruleSets`](#parameter-rulesets) | array | Array of rule set objects. | | [`secrets`](#parameter-secrets) | array | Array of secret objects. | +| [`securityPolicies`](#parameter-securitypolicies) | array | Array of Security Policy objects (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies for details). | | [`tags`](#parameter-tags) | object | Endpoint tags. | ### Parameter: `name` @@ -857,6 +1092,77 @@ Array of secret objects. - Type: array - Default: `[]` +### Parameter: `securityPolicies` + +Array of Security Policy objects (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies for details). + +- Required: No +- Type: array +- Default: `[]` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`associations`](#parameter-securitypoliciesassociations) | array | Domain names and URL patterns to math with this association. | +| [`name`](#parameter-securitypoliciesname) | string | Name of the security policy. | +| [`wafPolicyResourceId`](#parameter-securitypolicieswafpolicyresourceid) | string | Resource ID of WAF policy. | + +### Parameter: `securityPolicies.associations` + +Domain names and URL patterns to math with this association. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`domains`](#parameter-securitypoliciesassociationsdomains) | array | List of domain resource id to associate with this resource. | +| [`patternsToMatch`](#parameter-securitypoliciesassociationspatternstomatch) | array | List of patterns to match with this association. | + +### Parameter: `securityPolicies.associations.domains` + +List of domain resource id to associate with this resource. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`id`](#parameter-securitypoliciesassociationsdomainsid) | string | ResourceID to domain that will be associated. | + +### Parameter: `securityPolicies.associations.domains.id` + +ResourceID to domain that will be associated. + +- Required: Yes +- Type: string + +### Parameter: `securityPolicies.associations.patternsToMatch` + +List of patterns to match with this association. + +- Required: Yes +- Type: array + +### Parameter: `securityPolicies.name` + +Name of the security policy. + +- Required: Yes +- Type: string + +### Parameter: `securityPolicies.wafPolicyResourceId` + +Resource ID of WAF policy. + +- Required: Yes +- Type: string + ### Parameter: `tags` Endpoint tags. diff --git a/avm/res/cdn/profile/afdEndpoint/main.json b/avm/res/cdn/profile/afdEndpoint/main.json index 1f55d5cd6e..7cfef24e3f 100644 --- a/avm/res/cdn/profile/afdEndpoint/main.json +++ b/avm/res/cdn/profile/afdEndpoint/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3384292547879688658" + "version": "0.30.23.60470", + "templateHash": "792735746278824384" }, "name": "CDN Profiles AFD Endpoints", "description": "This module deploys a CDN Profile AFD Endpoint.", @@ -156,8 +156,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "18002678880456924020" + "version": "0.30.23.60470", + "templateHash": "1034122698174669197" }, "name": "CDN Profiles AFD Endpoint Route", "description": "This module deploys a CDN Profile AFD Endpoint route.", diff --git a/avm/res/cdn/profile/afdEndpoint/route/main.json b/avm/res/cdn/profile/afdEndpoint/route/main.json index 6a415af662..852e97f10c 100644 --- a/avm/res/cdn/profile/afdEndpoint/route/main.json +++ b/avm/res/cdn/profile/afdEndpoint/route/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "18002678880456924020" + "version": "0.30.23.60470", + "templateHash": "1034122698174669197" }, "name": "CDN Profiles AFD Endpoint Route", "description": "This module deploys a CDN Profile AFD Endpoint route.", diff --git a/avm/res/cdn/profile/customdomain/main.json b/avm/res/cdn/profile/customdomain/main.json index dd0a43d181..e45727e4ad 100644 --- a/avm/res/cdn/profile/customdomain/main.json +++ b/avm/res/cdn/profile/customdomain/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15211066835326278081" + "version": "0.30.23.60470", + "templateHash": "16955838730426729961" }, "name": "CDN Profiles Custom Domains", "description": "This module deploys a CDN Profile Custom Domains.", diff --git a/avm/res/cdn/profile/endpoint/main.json b/avm/res/cdn/profile/endpoint/main.json index 2fa89e8711..273dbe9fce 100644 --- a/avm/res/cdn/profile/endpoint/main.json +++ b/avm/res/cdn/profile/endpoint/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "6371656015674390162" + "version": "0.30.23.60470", + "templateHash": "3460565146034921053" }, "name": "CDN Profiles Endpoints", "description": "This module deploys a CDN Profile Endpoint.", @@ -125,8 +125,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11976988406992266750" + "version": "0.30.23.60470", + "templateHash": "4151069688274070352" }, "name": "CDN Profiles Endpoints Origins", "description": "This module deploys a CDN Profile Endpoint Origin.", diff --git a/avm/res/cdn/profile/endpoint/origin/main.json b/avm/res/cdn/profile/endpoint/origin/main.json index 13a2f8b35d..f4c079ff44 100644 --- a/avm/res/cdn/profile/endpoint/origin/main.json +++ b/avm/res/cdn/profile/endpoint/origin/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11976988406992266750" + "version": "0.30.23.60470", + "templateHash": "4151069688274070352" }, "name": "CDN Profiles Endpoints Origins", "description": "This module deploys a CDN Profile Endpoint Origin.", diff --git a/avm/res/cdn/profile/main.bicep b/avm/res/cdn/profile/main.bicep index c573133232..87323ba79f 100644 --- a/avm/res/cdn/profile/main.bicep +++ b/avm/res/cdn/profile/main.bicep @@ -49,6 +49,9 @@ param ruleSets array = [] @description('Optional. Array of AFD endpoint objects.') param afdEndpoints array = [] +@description('Optional. Array of Security Policy objects (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies for details).') +param securityPolicies securityPolicyType = [] + @description('Optional. Endpoint tags.') param tags object? @@ -251,6 +254,22 @@ module profile_afdEndpoints 'afdEndpoint/main.bicep' = [ } ] +module profile_securityPolicies 'securityPolicies/main.bicep' = [ + for (securityPolicy, index) in securityPolicies: { + name: '${uniqueString(deployment().name)}-Profile-SecurityPolicy-${index}' + dependsOn: [ + profile_afdEndpoints + profile_customDomains + ] + params: { + name: securityPolicy.name + profileName: profile.name + associations: securityPolicy.associations + wafPolicyResourceId: securityPolicy.wafPolicyResourceId + } + } +] + @description('The name of the CDN profile.') output name string = profile.name @@ -279,6 +298,18 @@ output uri string = !empty(endpointProperties) ? profile_endpoint.outputs.uri : // Definitions // // =============== // +import { associationsType } from 'securityPolicies/main.bicep' +type securityPolicyType = { + @description('Required. Name of the security policy.') + name: string + + @description('Required. Domain names and URL patterns to math with this association.') + associations: associationsType + + @description('Required. Resource ID of WAF policy.') + wafPolicyResourceId: string +}[] + type lockType = { @description('Optional. Specify the name of lock.') name: string? diff --git a/avm/res/cdn/profile/main.json b/avm/res/cdn/profile/main.json index 965bf9cb4c..169cee8564 100644 --- a/avm/res/cdn/profile/main.json +++ b/avm/res/cdn/profile/main.json @@ -5,14 +5,40 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "16789354290120442948" + "version": "0.30.23.60470", + "templateHash": "18013902785904421717" }, "name": "CDN Profiles", "description": "This module deploys a CDN Profile.", "owner": "Azure/module-maintainers" }, "definitions": { + "securityPolicyType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the security policy." + } + }, + "associations": { + "$ref": "#/definitions/associationsType", + "metadata": { + "description": "Required. Domain names and URL patterns to math with this association." + } + }, + "wafPolicyResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of WAF policy." + } + } + } + } + }, "lockType": { "type": "object", "properties": { @@ -110,6 +136,45 @@ } }, "nullable": true + }, + "associationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "domains": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. ResourceID to domain that will be associated." + } + } + } + }, + "metadata": { + "description": "Required. List of domain resource id to associate with this resource." + } + }, + "patternsToMatch": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. List of patterns to match with this association." + } + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "securityPolicies/main.bicep" + } + } } }, "parameters": { @@ -202,6 +267,13 @@ "description": "Optional. Array of AFD endpoint objects." } }, + "securityPolicies": { + "$ref": "#/definitions/securityPolicyType", + "defaultValue": [], + "metadata": { + "description": "Optional. Array of Security Policy objects (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies for details)." + } + }, "tags": { "type": "object", "nullable": true, @@ -350,8 +422,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "6371656015674390162" + "version": "0.30.23.60470", + "templateHash": "3460565146034921053" }, "name": "CDN Profiles Endpoints", "description": "This module deploys a CDN Profile Endpoint.", @@ -470,8 +542,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "11976988406992266750" + "version": "0.30.23.60470", + "templateHash": "4151069688274070352" }, "name": "CDN Profiles Endpoints Origins", "description": "This module deploys a CDN Profile Endpoint Origin.", @@ -723,8 +795,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "533126228291817357" + "version": "0.30.23.60470", + "templateHash": "7661706938502506866" }, "name": "CDN Profiles Secret", "description": "This module deploys a CDN Profile Secret.", @@ -872,8 +944,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "15211066835326278081" + "version": "0.30.23.60470", + "templateHash": "16955838730426729961" }, "name": "CDN Profiles Custom Domains", "description": "This module deploys a CDN Profile Custom Domains.", @@ -1039,8 +1111,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1182415535491789973" + "version": "0.30.23.60470", + "templateHash": "16948516107556143812" }, "name": "CDN Profiles Origin Group", "description": "This module deploys a CDN Profile Origin Group.", @@ -1176,8 +1248,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "14493731512795008787" + "version": "0.30.23.60470", + "templateHash": "4669077701065465911" }, "name": "CDN Profiles Origin", "description": "This module deploys a CDN Profile Origin.", @@ -1402,8 +1474,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "9792708426765797662" + "version": "0.30.23.60470", + "templateHash": "11520922481694023973" }, "name": "CDN Profiles Rule Sets", "description": "This module deploys a CDN Profile rule set.", @@ -1488,8 +1560,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "12904222825428666192" + "version": "0.30.23.60470", + "templateHash": "8818585542646204223" }, "name": "CDN Profiles Rules", "description": "This module deploys a CDN Profile rule.", @@ -1676,8 +1748,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3384292547879688658" + "version": "0.30.23.60470", + "templateHash": "792735746278824384" }, "name": "CDN Profiles AFD Endpoints", "description": "This module deploys a CDN Profile AFD Endpoint.", @@ -1827,8 +1899,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "18002678880456924020" + "version": "0.30.23.60470", + "templateHash": "1034122698174669197" }, "name": "CDN Profiles AFD Endpoint Route", "description": "This module deploys a CDN Profile AFD Endpoint route.", @@ -2119,6 +2191,168 @@ "profile_originGroups", "profile_ruleSets" ] + }, + "profile_securityPolicies": { + "copy": { + "name": "profile_securityPolicies", + "count": "[length(parameters('securityPolicies'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-Profile-SecurityPolicy-{1}', uniqueString(deployment().name), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[parameters('securityPolicies')[copyIndex()].name]" + }, + "profileName": { + "value": "[parameters('name')]" + }, + "associations": { + "value": "[parameters('securityPolicies')[copyIndex()].associations]" + }, + "wafPolicyResourceId": { + "value": "[parameters('securityPolicies')[copyIndex()].wafPolicyResourceId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.30.23.60470", + "templateHash": "11561080659040848436" + }, + "name": "CDN Profiles Security Policy", + "description": "This module deploys a CDN Profile Security Policy.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "associationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "domains": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. ResourceID to domain that will be associated." + } + } + } + }, + "metadata": { + "description": "Required. List of domain resource id to associate with this resource." + } + }, + "patternsToMatch": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. List of patterns to match with this association." + } + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The resource name." + } + }, + "profileName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent CDN profile. Required if the template is used in a standalone deployment." + } + }, + "wafPolicyResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of WAF Policy." + } + }, + "associations": { + "$ref": "#/definitions/associationsType", + "metadata": { + "description": "Required. Waf associations (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies?pivots=deployment-language-bicep#securitypolicywebapplicationfirewallassociation for details)." + } + } + }, + "resources": { + "profile": { + "existing": true, + "type": "Microsoft.Cdn/profiles", + "apiVersion": "2023-05-01", + "name": "[parameters('profileName')]" + }, + "securityPolicies": { + "type": "Microsoft.Cdn/profiles/securityPolicies", + "apiVersion": "2024-02-01", + "name": "[format('{0}/{1}', parameters('profileName'), parameters('name'))]", + "properties": { + "parameters": { + "type": "WebApplicationFirewall", + "wafPolicy": { + "id": "[parameters('wafPolicyResourceId')]" + }, + "associations": "[parameters('associations')]" + } + }, + "dependsOn": [ + "profile" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the secrect." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the secrect." + }, + "value": "[resourceId('Microsoft.Cdn/profiles/securityPolicies', parameters('profileName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the secret was created in." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "profile", + "profile_afdEndpoints", + "profile_customDomains" + ] } }, "outputs": { diff --git a/avm/res/cdn/profile/origingroup/main.json b/avm/res/cdn/profile/origingroup/main.json index 9a388bc48e..af9a692a27 100644 --- a/avm/res/cdn/profile/origingroup/main.json +++ b/avm/res/cdn/profile/origingroup/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "1182415535491789973" + "version": "0.30.23.60470", + "templateHash": "16948516107556143812" }, "name": "CDN Profiles Origin Group", "description": "This module deploys a CDN Profile Origin Group.", @@ -142,8 +142,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "14493731512795008787" + "version": "0.30.23.60470", + "templateHash": "4669077701065465911" }, "name": "CDN Profiles Origin", "description": "This module deploys a CDN Profile Origin.", diff --git a/avm/res/cdn/profile/origingroup/origin/main.json b/avm/res/cdn/profile/origingroup/origin/main.json index 56306ce135..8ee5bf04df 100644 --- a/avm/res/cdn/profile/origingroup/origin/main.json +++ b/avm/res/cdn/profile/origingroup/origin/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "14493731512795008787" + "version": "0.30.23.60470", + "templateHash": "4669077701065465911" }, "name": "CDN Profiles Origin", "description": "This module deploys a CDN Profile Origin.", diff --git a/avm/res/cdn/profile/ruleset/main.json b/avm/res/cdn/profile/ruleset/main.json index 47ff335b1c..2d040690b5 100644 --- a/avm/res/cdn/profile/ruleset/main.json +++ b/avm/res/cdn/profile/ruleset/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "9792708426765797662" + "version": "0.30.23.60470", + "templateHash": "11520922481694023973" }, "name": "CDN Profiles Rule Sets", "description": "This module deploys a CDN Profile rule set.", @@ -91,8 +91,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "12904222825428666192" + "version": "0.30.23.60470", + "templateHash": "8818585542646204223" }, "name": "CDN Profiles Rules", "description": "This module deploys a CDN Profile rule.", diff --git a/avm/res/cdn/profile/ruleset/rule/main.json b/avm/res/cdn/profile/ruleset/rule/main.json index bb37297681..98e0f0fef8 100644 --- a/avm/res/cdn/profile/ruleset/rule/main.json +++ b/avm/res/cdn/profile/ruleset/rule/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "12904222825428666192" + "version": "0.30.23.60470", + "templateHash": "8818585542646204223" }, "name": "CDN Profiles Rules", "description": "This module deploys a CDN Profile rule.", diff --git a/avm/res/cdn/profile/secret/main.json b/avm/res/cdn/profile/secret/main.json index a23afb02a0..6c300587db 100644 --- a/avm/res/cdn/profile/secret/main.json +++ b/avm/res/cdn/profile/secret/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "533126228291817357" + "version": "0.30.23.60470", + "templateHash": "7661706938502506866" }, "name": "CDN Profiles Secret", "description": "This module deploys a CDN Profile Secret.", diff --git a/avm/res/cdn/profile/securityPolicies/README.md b/avm/res/cdn/profile/securityPolicies/README.md new file mode 100644 index 0000000000..c30d3c5dad --- /dev/null +++ b/avm/res/cdn/profile/securityPolicies/README.md @@ -0,0 +1,101 @@ +# CDN Profiles Security Policy `[Microsoft.Cdn/profiles/securityPolicies]` + +This module deploys a CDN Profile Security Policy. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Cdn/profiles/securityPolicies` | [2024-02-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Cdn/profiles/securityPolicies) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`associations`](#parameter-associations) | array | Waf associations (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies?pivots=deployment-language-bicep#securitypolicywebapplicationfirewallassociation for details). | +| [`name`](#parameter-name) | string | The resource name. | +| [`wafPolicyResourceId`](#parameter-wafpolicyresourceid) | string | Resource ID of WAF Policy. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`profileName`](#parameter-profilename) | string | The name of the parent CDN profile. Required if the template is used in a standalone deployment. | + +### Parameter: `associations` + +Waf associations (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies?pivots=deployment-language-bicep#securitypolicywebapplicationfirewallassociation for details). + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`domains`](#parameter-associationsdomains) | array | List of domain resource id to associate with this resource. | +| [`patternsToMatch`](#parameter-associationspatternstomatch) | array | List of patterns to match with this association. | + +### Parameter: `associations.domains` + +List of domain resource id to associate with this resource. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`id`](#parameter-associationsdomainsid) | string | ResourceID to domain that will be associated. | + +### Parameter: `associations.domains.id` + +ResourceID to domain that will be associated. + +- Required: Yes +- Type: string + +### Parameter: `associations.patternsToMatch` + +List of patterns to match with this association. + +- Required: Yes +- Type: array + +### Parameter: `name` + +The resource name. + +- Required: Yes +- Type: string + +### Parameter: `wafPolicyResourceId` + +Resource ID of WAF Policy. + +- Required: Yes +- Type: string + +### Parameter: `profileName` + +The name of the parent CDN profile. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the secrect. | +| `resourceGroupName` | string | The name of the resource group the secret was created in. | +| `resourceId` | string | The resource ID of the secrect. | diff --git a/avm/res/cdn/profile/securityPolicies/main.bicep b/avm/res/cdn/profile/securityPolicies/main.bicep new file mode 100644 index 0000000000..1d98e235d9 --- /dev/null +++ b/avm/res/cdn/profile/securityPolicies/main.bicep @@ -0,0 +1,54 @@ +metadata name = 'CDN Profiles Security Policy' +metadata description = 'This module deploys a CDN Profile Security Policy.' +metadata owner = 'Azure/module-maintainers' + +@description('Required. The resource name.') +param name string + +@description('Conditional. The name of the parent CDN profile. Required if the template is used in a standalone deployment.') +param profileName string + +@description('Required. Resource ID of WAF Policy.') +param wafPolicyResourceId string + +// param associations associationsType +@description('Required. Waf associations (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies?pivots=deployment-language-bicep#securitypolicywebapplicationfirewallassociation for details).') +param associations associationsType + +resource profile 'Microsoft.Cdn/profiles@2023-05-01' existing = { + name: profileName +} + +resource securityPolicies 'Microsoft.Cdn/profiles/securityPolicies@2024-02-01' = { + name: name + parent: profile + properties: { + parameters: { + type: 'WebApplicationFirewall' + wafPolicy: { + id: wafPolicyResourceId + } + associations: associations + } + } +} + +@export() +type associationsType = { + @description('Required. List of domain resource id to associate with this resource.') + domains: { + @description('Required. ResourceID to domain that will be associated.') + id: string + }[] + @description('Required. List of patterns to match with this association.') + patternsToMatch: string[] +}[] + +@description('The name of the secrect.') +output name string = securityPolicies.name + +@description('The resource ID of the secrect.') +output resourceId string = securityPolicies.id + +@description('The name of the resource group the secret was created in.') +output resourceGroupName string = resourceGroup().name diff --git a/avm/res/cdn/profile/securityPolicies/main.json b/avm/res/cdn/profile/securityPolicies/main.json new file mode 100644 index 0000000000..be06e14c99 --- /dev/null +++ b/avm/res/cdn/profile/securityPolicies/main.json @@ -0,0 +1,128 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.30.23.60470", + "templateHash": "11561080659040848436" + }, + "name": "CDN Profiles Security Policy", + "description": "This module deploys a CDN Profile Security Policy.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "associationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "domains": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "metadata": { + "description": "Required. ResourceID to domain that will be associated." + } + } + } + }, + "metadata": { + "description": "Required. List of domain resource id to associate with this resource." + } + }, + "patternsToMatch": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. List of patterns to match with this association." + } + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The resource name." + } + }, + "profileName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent CDN profile. Required if the template is used in a standalone deployment." + } + }, + "wafPolicyResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of WAF Policy." + } + }, + "associations": { + "$ref": "#/definitions/associationsType", + "metadata": { + "description": "Required. Waf associations (see https://learn.microsoft.com/en-us/azure/templates/microsoft.cdn/profiles/securitypolicies?pivots=deployment-language-bicep#securitypolicywebapplicationfirewallassociation for details)." + } + } + }, + "resources": { + "profile": { + "existing": true, + "type": "Microsoft.Cdn/profiles", + "apiVersion": "2023-05-01", + "name": "[parameters('profileName')]" + }, + "securityPolicies": { + "type": "Microsoft.Cdn/profiles/securityPolicies", + "apiVersion": "2024-02-01", + "name": "[format('{0}/{1}', parameters('profileName'), parameters('name'))]", + "properties": { + "parameters": { + "type": "WebApplicationFirewall", + "wafPolicy": { + "id": "[parameters('wafPolicyResourceId')]" + }, + "associations": "[parameters('associations')]" + } + }, + "dependsOn": [ + "profile" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the secrect." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the secrect." + }, + "value": "[resourceId('Microsoft.Cdn/profiles/securityPolicies', parameters('profileName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the secret was created in." + }, + "value": "[resourceGroup().name]" + } + } +} \ No newline at end of file diff --git a/avm/res/cdn/profile/tests/e2e/afd.premium/main.test.bicep b/avm/res/cdn/profile/tests/e2e/afd.premium/main.test.bicep new file mode 100644 index 0000000000..e735b9c94b --- /dev/null +++ b/avm/res/cdn/profile/tests/e2e/afd.premium/main.test.bicep @@ -0,0 +1,146 @@ +targetScope = 'subscription' + +metadata name = 'As Azure Front Door Premium' +metadata description = 'This instance deploys the module as Azure Front Door Premium.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-cdn.profiles-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'cdnpafdp' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module wafPolicy 'br/public:avm/res/network/front-door-web-application-firewall-policy:0.2.0' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-dep-waf-policy-${serviceShort}' + params: { + name: 'dep${namePrefix}${serviceShort}wafpolicy' + sku: 'Premium_AzureFrontDoor' + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: 'dep-${namePrefix}-test-${serviceShort}' + location: 'global' + originResponseTimeoutSeconds: 60 + sku: 'Premium_AzureFrontDoor' + customDomains: [ + { + name: 'dep-${namePrefix}-test-${serviceShort}-custom-domain' + hostName: 'dep-${namePrefix}-test-${serviceShort}-custom-domain.azurewebsites.net' + certificateType: 'ManagedCertificate' + } + ] + originGroups: [ + { + name: 'dep-${namePrefix}-test-${serviceShort}-origin-group' + loadBalancingSettings: { + additionalLatencyInMilliseconds: 50 + sampleSize: 4 + successfulSamplesRequired: 3 + } + origins: [ + { + name: 'dep-${namePrefix}-test-${serviceShort}-origin' + hostName: 'dep-${namePrefix}-test-${serviceShort}-origin.azurewebsites.net' + } + ] + } + ] + ruleSets: [ + { + name: 'dep${namePrefix}test${serviceShort}ruleset' + rules: [ + { + name: 'dep${namePrefix}test${serviceShort}rule' + order: 1 + actions: [ + { + name: 'UrlRedirect' + parameters: { + typeName: 'DeliveryRuleUrlRedirectActionParameters' + redirectType: 'PermanentRedirect' + destinationProtocol: 'Https' + customPath: '/test123' + customHostname: 'dev-etradefd.trade.azure.defra.cloud' + } + } + ] + } + ] + } + ] + afdEndpoints: [ + { + name: 'dep-${namePrefix}-test-${serviceShort}-afd-endpoint' + routes: [ + { + name: 'dep-${namePrefix}-test-${serviceShort}-afd-route' + originGroupName: 'dep-${namePrefix}-test-${serviceShort}-origin-group' + customDomainNames: ['dep-${namePrefix}-test-${serviceShort}-custom-domain'] + ruleSets: [ + { + name: 'dep${namePrefix}test${serviceShort}ruleset' + } + ] + } + ] + } + ] + securityPolicies: [ + { + name: 'dep${namePrefix}test${serviceShort}secpol' + associations: [ + { + domains: [ + { + id: resourceId( + subscription().subscriptionId, + resourceGroup.name, + 'Microsoft.Cdn/profiles/afdEndpoints', + 'dep-${namePrefix}-test-${serviceShort}', + 'dep-${namePrefix}-test-${serviceShort}-afd-endpoint' + ) + } + ] + patternsToMatch: [ + '/*' + ] + } + ] + wafPolicyResourceId: wafPolicy.outputs.resourceId + } + ] + } + } +] diff --git a/avm/res/cdn/profile/version.json b/avm/res/cdn/profile/version.json index a8eda31021..9ed3662aba 100644 --- a/avm/res/cdn/profile/version.json +++ b/avm/res/cdn/profile/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.5", + "version": "0.6", "pathFilters": [ "./main.json" ] -} \ No newline at end of file +} From 7d6600c6f44a279832d5bd7497fed0dc617e89eb Mon Sep 17 00:00:00 2001 From: mortenvpschmidt <47330927+mortenvpschmidt@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:34:30 +0200 Subject: [PATCH 51/57] fix: Add key vault to search service - `avm/res/search/search-service` (#1950) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add option for outputting admin keys to search service ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.search.search-service](https://github.com/mortenvpschmidt/bicep-registry-modules/actions/workflows/avm.res.search.search-service.yml/badge.svg?branch=Aisearch_w_keyvault)](https://github.com/mortenvpschmidt/bicep-registry-modules/actions/workflows/avm.res.search.search-service.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module effecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --------- Co-authored-by: Morten Schmidt Co-authored-by: Axel Bøg Andersen Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com> --- avm/res/search/search-service/README.md | 138 ++++++++++- avm/res/search/search-service/main.bicep | 64 ++++- avm/res/search/search-service/main.json | 223 ++++++++++++++++-- .../modules/keyVaultExport.bicep | 62 +++++ .../tests/e2e/kvSecrets/dependencies.bicep | 21 ++ .../tests/e2e/kvSecrets/main.test.bicep | 62 +++++ .../tests/e2e/pe/main.test.bicep | 2 +- 7 files changed, 535 insertions(+), 37 deletions(-) create mode 100644 avm/res/search/search-service/modules/keyVaultExport.bicep create mode 100644 avm/res/search/search-service/tests/e2e/kvSecrets/dependencies.bicep create mode 100644 avm/res/search/search-service/tests/e2e/kvSecrets/main.test.bicep diff --git a/avm/res/search/search-service/README.md b/avm/res/search/search-service/README.md index 9001f0359c..e8645d04a5 100644 --- a/avm/res/search/search-service/README.md +++ b/avm/res/search/search-service/README.md @@ -18,6 +18,7 @@ This module deploys a Search Service. | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | | `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.KeyVault/vaults/secrets` | [2023-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2023-07-01/vaults/secrets) | | `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | | `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | | `Microsoft.Search/searchServices` | [2024-03-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Search/2024-03-01-preview/searchServices) | @@ -32,9 +33,10 @@ The following section provides usage examples for the module, which were used to >**Note**: To reference the module, please use the following syntax `br/public:avm/res/search/search-service:`. - [Using only defaults](#example-1-using-only-defaults) -- [Using large parameter set](#example-2-using-large-parameter-set) -- [Private endpoint-enabled deployment](#example-3-private-endpoint-enabled-deployment) -- [WAF-aligned](#example-4-waf-aligned) +- [Deploying with a key vault reference to save secrets](#example-2-deploying-with-a-key-vault-reference-to-save-secrets) +- [Using large parameter set](#example-3-using-large-parameter-set) +- [Private endpoint-enabled deployment](#example-4-private-endpoint-enabled-deployment) +- [WAF-aligned](#example-5-waf-aligned) ### Example 1: _Using only defaults_ @@ -84,7 +86,83 @@ module searchService 'br/public:avm/res/search/search-service:' = {

-### Example 2: _Using large parameter set_ +### Example 2: _Deploying with a key vault reference to save secrets_ + +This instance deploys the module saving admin key secrets in a key vault. + + +

+ +via Bicep module + +```bicep +module searchService 'br/public:avm/res/search/search-service:' = { + name: 'searchServiceDeployment' + params: { + // Required parameters + name: 'kv-ref' + // Non-required parameters + authOptions: { + aadOrApiKey: { + aadAuthFailureMode: 'http401WithBearerChallenge' + } + } + disableLocalAuth: false + location: '' + secretsExportConfiguration: { + keyVaultResourceId: '' + primaryAdminKeyName: 'Primary-Admin-Key' + secondaryAdminKeyName: 'Secondary-Admin-Key' + } + } +} +``` + +
+

+ +

+ +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": "kv-ref" + }, + // Non-required parameters + "authOptions": { + "value": { + "aadOrApiKey": { + "aadAuthFailureMode": "http401WithBearerChallenge" + } + } + }, + "disableLocalAuth": { + "value": false + }, + "location": { + "value": "" + }, + "secretsExportConfiguration": { + "value": { + "keyVaultResourceId": "", + "primaryAdminKeyName": "Primary-Admin-Key", + "secondaryAdminKeyName": "Secondary-Admin-Key" + } + } + } +} +``` + +
+

+ +### Example 3: _Using large parameter set_ This instance deploys the module with most of its features enabled. @@ -300,7 +378,7 @@ module searchService 'br/public:avm/res/search/search-service:' = {

-### Example 3: _Private endpoint-enabled deployment_ +### Example 4: _Private endpoint-enabled deployment_ This instance deploys the module with private endpoints. @@ -314,7 +392,7 @@ module searchService 'br/public:avm/res/search/search-service:' = { name: 'searchServiceDeployment' params: { // Required parameters - name: 'ssspe001' + name: 'ssspr001' // Non-required parameters location: '' privateEndpoints: [ @@ -383,7 +461,7 @@ module searchService 'br/public:avm/res/search/search-service:' = { "parameters": { // Required parameters "name": { - "value": "ssspe001" + "value": "ssspr001" }, // Non-required parameters "location": { @@ -452,7 +530,7 @@ module searchService 'br/public:avm/res/search/search-service:' = {

-### Example 4: _WAF-aligned_ +### Example 5: _WAF-aligned_ This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework. @@ -645,6 +723,7 @@ module searchService 'br/public:avm/res/search/search-service:' = { | [`publicNetworkAccess`](#parameter-publicnetworkaccess) | string | This value can be set to 'Enabled' to avoid breaking changes on existing customer resources and templates. If set to 'Disabled', traffic over public interface is not allowed, and private endpoint connections would be the exclusive access method. | | [`replicaCount`](#parameter-replicacount) | int | The number of replicas in the search service. If specified, it must be a value between 1 and 12 inclusive for standard SKUs or between 1 and 3 inclusive for basic SKU. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`secretsExportConfiguration`](#parameter-secretsexportconfiguration) | object | Key vault reference and secret settings for the module's secrets export. | | [`semanticSearch`](#parameter-semanticsearch) | string | Sets options that control the availability of semantic search. This configuration is only possible for certain search SKUs in certain locations. | | [`sharedPrivateLinkResources`](#parameter-sharedprivatelinkresources) | array | The sharedPrivateLinkResources to create as part of the search Service. | | [`sku`](#parameter-sku) | string | Defines the SKU of an Azure Cognitive Search Service, which determines price tier and capacity limits. | @@ -1481,6 +1560,47 @@ The principal type of the assigned principal ID. ] ``` +### Parameter: `secretsExportConfiguration` + +Key vault reference and secret settings for the module's secrets export. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`keyVaultResourceId`](#parameter-secretsexportconfigurationkeyvaultresourceid) | string | The key vault name where to store the API Admin keys generated by the modules. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`primaryAdminKeyName`](#parameter-secretsexportconfigurationprimaryadminkeyname) | string | The primaryAdminKey secret name to create. | +| [`secondaryAdminKeyName`](#parameter-secretsexportconfigurationsecondaryadminkeyname) | string | The secondaryAdminKey secret name to create. | + +### Parameter: `secretsExportConfiguration.keyVaultResourceId` + +The key vault name where to store the API Admin keys generated by the modules. + +- Required: Yes +- Type: string + +### Parameter: `secretsExportConfiguration.primaryAdminKeyName` + +The primaryAdminKey secret name to create. + +- Required: No +- Type: string + +### Parameter: `secretsExportConfiguration.secondaryAdminKeyName` + +The secondaryAdminKey secret name to create. + +- Required: No +- Type: string + ### Parameter: `semanticSearch` Sets options that control the availability of semantic search. This configuration is only possible for certain search SKUs in certain locations. @@ -1535,9 +1655,9 @@ Tags to help categorize the resource in the Azure portal. | Output | Type | Description | | :-- | :-- | :-- | +| `exportedSecrets` | | A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name. | | `location` | string | The location the resource was deployed into. | | `name` | string | The name of the search service. | -| `privateEndpoints` | array | The private endpoints of the search service. | | `resourceGroupName` | string | The name of the resource group the search service was created in. | | `resourceId` | string | The resource ID of the search service. | | `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. | diff --git a/avm/res/search/search-service/main.bicep b/avm/res/search/search-service/main.bicep index 951adfe725..df608c8af6 100644 --- a/avm/res/search/search-service/main.bicep +++ b/avm/res/search/search-service/main.bicep @@ -60,6 +60,9 @@ param sharedPrivateLinkResources array = [] ]) param publicNetworkAccess string = 'Enabled' +@description('Optional. Key vault reference and secret settings for the module\'s secrets export.') +param secretsExportConfiguration secretsExportConfigurationType? + @description('Optional. The number of replicas in the search service. If specified, it must be a value between 1 and 12 inclusive for standard SKUs or between 1 and 3 inclusive for basic SKU.') @minValue(1) @maxValue(12) @@ -324,6 +327,36 @@ module searchService_sharedPrivateLinkResources 'shared-private-link-resource/ma } ] +module secretsExport 'modules/keyVaultExport.bicep' = if (secretsExportConfiguration != null) { + name: '${uniqueString(deployment().name, location)}-secrets-kv' + scope: resourceGroup( + split((secretsExportConfiguration.?keyVaultResourceId ?? '//'), '/')[2], + split((secretsExportConfiguration.?keyVaultResourceId ?? '////'), '/')[4] + ) + params: { + keyVaultName: last(split(secretsExportConfiguration.?keyVaultResourceId ?? '//', '/')) + secretsToSet: union( + [], + contains(secretsExportConfiguration!, 'primaryAdminKeyName') + ? [ + { + name: secretsExportConfiguration!.primaryAdminKeyName + value: searchService.listAdminKeys().primaryKey + } + ] + : [], + contains(secretsExportConfiguration!, 'secondaryAdminKeyName') + ? [ + { + name: secretsExportConfiguration!.secondaryAdminKeyName + value: searchService.listAdminKeys().secondaryKey + } + ] + : [] + ) + } +} + // =========== // // Outputs // // =========== // @@ -343,16 +376,10 @@ output systemAssignedMIPrincipalId string = searchService.?identity.?principalId @description('The location the resource was deployed into.') output location string = searchService.location -@description('The private endpoints of the search service.') -output privateEndpoints array = [ - for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { - name: searchService_privateEndpoints[i].outputs.name - resourceId: searchService_privateEndpoints[i].outputs.resourceId - groupId: searchService_privateEndpoints[i].outputs.groupId - customDnsConfig: searchService_privateEndpoints[i].outputs.customDnsConfig - networkInterfaceIds: searchService_privateEndpoints[i].outputs.networkInterfaceIds - } -] +@description('A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret\'s name.') +output exportedSecrets secretsOutputType = (secretsExportConfiguration != null) + ? toObject(secretsExport.outputs.secretsSet, secret => last(split(secret.secretResourceId, '/')), secret => secret) + : {} // =============== // // Definitions // @@ -530,3 +557,20 @@ type diagnosticSettingType = { @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') marketplacePartnerResourceId: string? }[]? + +type secretsExportConfigurationType = { + @description('Required. The key vault name where to store the API Admin keys generated by the modules.') + keyVaultResourceId: string + + @description('Optional. The primaryAdminKey secret name to create.') + primaryAdminKeyName: string? + + @description('Optional. The secondaryAdminKey secret name to create.') + secondaryAdminKeyName: string? +} + +import { secretSetType } from 'modules/keyVaultExport.bicep' +type secretsOutputType = { + @description('An exported secret\'s references.') + *: secretSetType +} diff --git a/avm/res/search/search-service/main.json b/avm/res/search/search-service/main.json index c4b625ff47..24719a5ca7 100644 --- a/avm/res/search/search-service/main.json +++ b/avm/res/search/search-service/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "8483667347070963331" + "version": "0.30.23.60470", + "templateHash": "13527260085574333800" }, "name": "Search Services", "description": "This module deploys a Search Service.", @@ -473,6 +473,63 @@ } }, "nullable": true + }, + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The key vault name where to store the API Admin keys generated by the modules." + } + }, + "primaryAdminKeyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The primaryAdminKey secret name to create." + } + }, + "secondaryAdminKeyName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The secondaryAdminKey secret name to create." + } + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/secretSetType", + "metadata": { + "description": "An exported secret's references." + } + } + }, + "secretSetType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "modules/keyVaultExport.bicep" + } + } } }, "parameters": { @@ -579,6 +636,13 @@ "description": "Optional. This value can be set to 'Enabled' to avoid breaking changes on existing customer resources and templates. If set to 'Disabled', traffic over public interface is not allowed, and private endpoint connections would be the exclusive access method." } }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, "replicaCount": { "type": "int", "defaultValue": 3, @@ -1593,8 +1657,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "2330033720810948871" + "version": "0.30.23.60470", + "templateHash": "1073269867332822875" }, "name": "Search Services Private Link Resources", "description": "This module deploys a Search Service Private Link Resource.", @@ -1689,6 +1753,140 @@ "dependsOn": [ "searchService" ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-secrets-kv', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '////'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'primaryAdminKeyName'), createArray(createObject('name', parameters('secretsExportConfiguration').primaryAdminKeyName, 'value', listAdminKeys(resourceId('Microsoft.Search/searchServices', parameters('name')), '2024-03-01-preview').primaryKey)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'secondaryAdminKeyName'), createArray(createObject('name', parameters('secretsExportConfiguration').secondaryAdminKeyName, 'value', listAdminKeys(resourceId('Microsoft.Search/searchServices', parameters('name')), '2024-03-01-preview').secondaryKey)), createArray()))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.30.23.60470", + "templateHash": "12263717469683062316" + } + }, + "definitions": { + "secretSetType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "secretToSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the ecrets in." + } + }, + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" + }, + "metadata": { + "description": "Required. The secrets to set in the Key Vault." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" + }, + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" + }, + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + }, + "dependsOn": [ + "keyVault" + ] + } + }, + "outputs": { + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetType" + }, + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]" + } + } + } + } + } + }, + "dependsOn": [ + "searchService" + ] } }, "outputs": { @@ -1727,21 +1925,12 @@ }, "value": "[reference('searchService', '2024-03-01-preview', 'full').location]" }, - "privateEndpoints": { - "type": "array", + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", "metadata": { - "description": "The private endpoints of the search service." + "description": "A hashtable of references to the secrets exported to the provided Key Vault. The key of each reference is each secret's name." }, - "copy": { - "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", - "input": { - "name": "[reference(format('searchService_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", - "resourceId": "[reference(format('searchService_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", - "groupId": "[reference(format('searchService_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", - "customDnsConfig": "[reference(format('searchService_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", - "networkInterfaceIds": "[reference(format('searchService_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" - } - } + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" } } } \ No newline at end of file diff --git a/avm/res/search/search-service/modules/keyVaultExport.bicep b/avm/res/search/search-service/modules/keyVaultExport.bicep new file mode 100644 index 0000000000..d537d2407e --- /dev/null +++ b/avm/res/search/search-service/modules/keyVaultExport.bicep @@ -0,0 +1,62 @@ +// ============== // +// Parameters // +// ============== // + +@description('Required. The name of the Key Vault to set the ecrets in.') +param keyVaultName string + +@description('Required. The secrets to set in the Key Vault.') +param secretsToSet secretToSetType[] + +// ============= // +// Resources // +// ============= // + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +resource secrets 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = [ + for secret in secretsToSet: { + name: secret.name + parent: keyVault + properties: { + value: secret.value + } + } +] + +// =========== // +// Outputs // +// =========== // + +@description('The references to the secrets exported to the provided Key Vault.') +output secretsSet secretSetType[] = [ + #disable-next-line outputs-should-not-contain-secrets // Only returning the references, not a secret value + for index in range(0, length(secretsToSet ?? [])): { + secretResourceId: secrets[index].id + secretUri: secrets[index].properties.secretUri + } +] + +// =============== // +// Definitions // +// =============== // + +@export() +type secretSetType = { + @description('The resourceId of the exported secret.') + secretResourceId: string + + @description('The secret URI of the exported secret.') + secretUri: string +} + +type secretToSetType = { + @description('Required. The name of the secret to set.') + name: string + + @description('Required. The value of the secret to set.') + @secure() + value: string +} diff --git a/avm/res/search/search-service/tests/e2e/kvSecrets/dependencies.bicep b/avm/res/search/search-service/tests/e2e/kvSecrets/dependencies.bicep new file mode 100644 index 0000000000..da0b29e26f --- /dev/null +++ b/avm/res/search/search-service/tests/e2e/kvSecrets/dependencies.bicep @@ -0,0 +1,21 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param keyVaultName string + +resource keyVault 'Microsoft.KeyVault/vaults@2021-06-01-preview' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + enableRbacAuthorization: true + tenantId: subscription().tenantId + } +} + +@description('The key vault id of the Key Vault created.') +output keyVaultResourceId string = keyVault.id diff --git a/avm/res/search/search-service/tests/e2e/kvSecrets/main.test.bicep b/avm/res/search/search-service/tests/e2e/kvSecrets/main.test.bicep new file mode 100644 index 0000000000..cdd93d36cd --- /dev/null +++ b/avm/res/search/search-service/tests/e2e/kvSecrets/main.test.bicep @@ -0,0 +1,62 @@ +targetScope = 'subscription' + +metadata name = 'Deploying with a key vault reference to save secrets' +metadata description = 'This instance deploys the module saving admin key secrets in a key vault.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-search.searchservices-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'ssskvs' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +// ============== // +// General resources +// ============== // +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + location: resourceLocation + name: '${namePrefix}-kv-ref' + disableLocalAuth: false + authOptions: { + aadOrApiKey: { + aadAuthFailureMode: 'http401WithBearerChallenge' + } + } + secretsExportConfiguration: { + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + primaryAdminKeyName: 'Primary-Admin-Key' + secondaryAdminKeyName: 'Secondary-Admin-Key' + } + } +} diff --git a/avm/res/search/search-service/tests/e2e/pe/main.test.bicep b/avm/res/search/search-service/tests/e2e/pe/main.test.bicep index 185cd85d5f..76fecb0274 100644 --- a/avm/res/search/search-service/tests/e2e/pe/main.test.bicep +++ b/avm/res/search/search-service/tests/e2e/pe/main.test.bicep @@ -15,7 +15,7 @@ param resourceGroupName string = 'dep-${namePrefix}-search.searchservices-${serv param resourceLocation string = deployment().location @description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') -param serviceShort string = 'ssspe' +param serviceShort string = 'ssspr' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' From 262267189fb68262ed529d90979d9d2ccc21f0b9 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Tue, 1 Oct 2024 16:36:27 +0200 Subject: [PATCH 52/57] feat: DataFactory - Added UDTs (#3117) ## Description Follow-up to #2763 --- Quote: @donheerschap Added UDTs for: Managed Private Endpoints Integration Runtimes Linked Services For a better dev experience. > Edit: @AlexanderSehr also addressed linter warnings ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.data-factory.factory](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.data-factory.factory.yml/badge.svg?branch=users%2Falsehr%2Fdonheerschap%2Fadd-adf-udts&event=workflow_dispatch)](https://github.com/Azure/bicep-registry-modules/actions/workflows/avm.res.data-factory.factory.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation --------- Co-authored-by: Don Heerschap --- avm/res/data-factory/factory/README.md | 161 ++++++++++++++++- .../factory/integration-runtime/main.json | 4 +- .../factory/linked-service/main.bicep | 1 + .../factory/linked-service/main.json | 4 +- avm/res/data-factory/factory/main.bicep | 68 ++++++- avm/res/data-factory/factory/main.json | 166 ++++++++++++++++-- .../factory/managed-virtual-network/main.json | 8 +- .../managed-private-endpoint/main.json | 4 +- .../factory/tests/e2e/max/main.test.bicep | 2 +- 9 files changed, 381 insertions(+), 37 deletions(-) diff --git a/avm/res/data-factory/factory/README.md b/avm/res/data-factory/factory/README.md index bd9ec6e1c6..4f12df906f 100644 --- a/avm/res/data-factory/factory/README.md +++ b/avm/res/data-factory/factory/README.md @@ -165,7 +165,7 @@ module factory 'br/public:avm/res/data-factory/factory:' = { } type: 'AzureBlobFS' typeProperties: { - url: '@{concat(\'https://\', linkedService().storageAccountName, \'.dfs.core.windows.net\')}' + url: '' } } ] @@ -335,7 +335,7 @@ module factory 'br/public:avm/res/data-factory/factory:' = { }, "type": "AzureBlobFS", "typeProperties": { - "url": "@{concat(\"https://\", linkedService().storageAccountName, \".dfs.core.windows.net\")}" + "url": "" } } ] @@ -884,6 +884,63 @@ An array of objects for the configuration of an Integration Runtime. - Type: array - Default: `[]` +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-integrationruntimesname) | string | Specify the name of integration runtime. | +| [`type`](#parameter-integrationruntimestype) | string | Specify the type of the integration runtime. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`integrationRuntimeCustomDescription`](#parameter-integrationruntimesintegrationruntimecustomdescription) | string | Specify custom description for the integration runtime. | +| [`managedVirtualNetworkName`](#parameter-integrationruntimesmanagedvirtualnetworkname) | string | Specify managed vritual network name for the integration runtime to link to. | +| [`typeProperties`](#parameter-integrationruntimestypeproperties) | object | Integration Runtime type properties. Required if type is "Managed". | + +### Parameter: `integrationRuntimes.name` + +Specify the name of integration runtime. + +- Required: Yes +- Type: string + +### Parameter: `integrationRuntimes.type` + +Specify the type of the integration runtime. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Managed' + 'SelfHosted' + ] + ``` + +### Parameter: `integrationRuntimes.integrationRuntimeCustomDescription` + +Specify custom description for the integration runtime. + +- Required: No +- Type: string + +### Parameter: `integrationRuntimes.managedVirtualNetworkName` + +Specify managed vritual network name for the integration runtime to link to. + +- Required: No +- Type: string + +### Parameter: `integrationRuntimes.typeProperties` + +Integration Runtime type properties. Required if type is "Managed". + +- Required: No +- Type: object + ### Parameter: `linkedServices` An array of objects for the configuration of Linked Services. @@ -892,6 +949,64 @@ An array of objects for the configuration of Linked Services. - Type: array - Default: `[]` +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-linkedservicesname) | string | The name of the Linked Service. | +| [`type`](#parameter-linkedservicestype) | string | The type of Linked Service. See https://learn.microsoft.com/en-us/azure/templates/microsoft.datafactory/factories/linkedservices?pivots=deployment-language-bicep#linkedservice-objects for more information. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`description`](#parameter-linkedservicesdescription) | string | The description of the Integration Runtime. | +| [`integrationRuntimeName`](#parameter-linkedservicesintegrationruntimename) | string | The name of the Integration Runtime to use. | +| [`parameters`](#parameter-linkedservicesparameters) | object | Use this to add parameters for a linked service connection string. | +| [`typeProperties`](#parameter-linkedservicestypeproperties) | object | Used to add connection properties for your linked services. | + +### Parameter: `linkedServices.name` + +The name of the Linked Service. + +- Required: Yes +- Type: string + +### Parameter: `linkedServices.type` + +The type of Linked Service. See https://learn.microsoft.com/en-us/azure/templates/microsoft.datafactory/factories/linkedservices?pivots=deployment-language-bicep#linkedservice-objects for more information. + +- Required: Yes +- Type: string + +### Parameter: `linkedServices.description` + +The description of the Integration Runtime. + +- Required: No +- Type: string + +### Parameter: `linkedServices.integrationRuntimeName` + +The name of the Integration Runtime to use. + +- Required: No +- Type: string + +### Parameter: `linkedServices.parameters` + +Use this to add parameters for a linked service connection string. + +- Required: No +- Type: object + +### Parameter: `linkedServices.typeProperties` + +Used to add connection properties for your linked services. + +- Required: No +- Type: object + ### Parameter: `location` Location for all Resources. @@ -972,6 +1087,48 @@ An array of managed private endpoints objects created in the Data Factory manage - Type: array - Default: `[]` +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`groupId`](#parameter-managedprivateendpointsgroupid) | string | Specify the sub-resource of the managed private endpoint. | +| [`name`](#parameter-managedprivateendpointsname) | string | Specify the name of managed private endpoint. | +| [`privateLinkResourceId`](#parameter-managedprivateendpointsprivatelinkresourceid) | string | Specify the resource ID to create the managed private endpoint for. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`fqdns`](#parameter-managedprivateendpointsfqdns) | array | Specify the FQDNS of the linked resources to create private endpoints for, depending on the type of linked resource this is required. | + +### Parameter: `managedPrivateEndpoints.groupId` + +Specify the sub-resource of the managed private endpoint. + +- Required: Yes +- Type: string + +### Parameter: `managedPrivateEndpoints.name` + +Specify the name of managed private endpoint. + +- Required: Yes +- Type: string + +### Parameter: `managedPrivateEndpoints.privateLinkResourceId` + +Specify the resource ID to create the managed private endpoint for. + +- Required: Yes +- Type: string + +### Parameter: `managedPrivateEndpoints.fqdns` + +Specify the FQDNS of the linked resources to create private endpoints for, depending on the type of linked resource this is required. + +- Required: No +- Type: array + ### Parameter: `managedVirtualNetworkName` The name of the Managed Virtual Network. diff --git a/avm/res/data-factory/factory/integration-runtime/main.json b/avm/res/data-factory/factory/integration-runtime/main.json index 991ffcaaa3..2db2b0af74 100644 --- a/avm/res/data-factory/factory/integration-runtime/main.json +++ b/avm/res/data-factory/factory/integration-runtime/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "7050455062079134223" + "version": "0.30.23.60470", + "templateHash": "17201448291064419524" }, "name": "Data Factory Integration RunTimes", "description": "This module deploys a Data Factory Managed or Self-Hosted Integration Runtime.", diff --git a/avm/res/data-factory/factory/linked-service/main.bicep b/avm/res/data-factory/factory/linked-service/main.bicep index af51a01544..5748e4582f 100644 --- a/avm/res/data-factory/factory/linked-service/main.bicep +++ b/avm/res/data-factory/factory/linked-service/main.bicep @@ -41,6 +41,7 @@ resource linkedService 'Microsoft.DataFactory/factories/linkedservices@2018-06-0 referenceName: integrationRuntimeName type: 'IntegrationRuntimeReference' } + #disable-next-line BCP225 // false-positive as 'type' is interpreted as a syntax value type: type typeProperties: typeProperties parameters: parameters diff --git a/avm/res/data-factory/factory/linked-service/main.json b/avm/res/data-factory/factory/linked-service/main.json index c44e38f1ed..12ca641d7d 100644 --- a/avm/res/data-factory/factory/linked-service/main.json +++ b/avm/res/data-factory/factory/linked-service/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "2964661488202892260" + "version": "0.30.23.60470", + "templateHash": "5456106394920661740" }, "name": "Data Factory Linked Service", "description": "This module deploys a Data Factory Linked Service.", diff --git a/avm/res/data-factory/factory/main.bicep b/avm/res/data-factory/factory/main.bicep index 7b24ccaf43..5bb934d5a0 100644 --- a/avm/res/data-factory/factory/main.bicep +++ b/avm/res/data-factory/factory/main.bicep @@ -9,13 +9,13 @@ param name string param managedVirtualNetworkName string = '' @description('Optional. An array of managed private endpoints objects created in the Data Factory managed virtual network.') -param managedPrivateEndpoints array = [] +param managedPrivateEndpoints managedPrivateEndpointType[] = [] @description('Optional. An array of objects for the configuration of an Integration Runtime.') -param integrationRuntimes array = [] +param integrationRuntimes integrationRuntimesType = [] @description('Optional. An array of objects for the configuration of Linked Services.') -param linkedServices array = [] +param linkedServices linkedServicesType = [] @description('Optional. Location for all Resources.') param location string = resourceGroup().location @@ -235,11 +235,9 @@ module dataFactory_integrationRuntimes 'integration-runtime/main.bicep' = [ dataFactoryName: dataFactory.name name: integrationRuntime.name type: integrationRuntime.type - integrationRuntimeCustomDescription: integrationRuntime.?integrationRuntimeCustomDescription ?? 'Managed Integration Runtime created by avm-res-datafactory-factories' - managedVirtualNetworkName: contains(integrationRuntime, 'managedVirtualNetworkName') - ? integrationRuntime.managedVirtualNetworkName - : '' - typeProperties: contains(integrationRuntime, 'typeProperties') ? integrationRuntime.typeProperties : {} + integrationRuntimeCustomDescription: integrationRuntime.?integrationRuntimeCustomDescription + managedVirtualNetworkName: integrationRuntime.?managedVirtualNetworkName + typeProperties: integrationRuntime.?typeProperties } dependsOn: [ dataFactory_managedVirtualNetwork @@ -259,6 +257,9 @@ module dataFactory_linkedServices 'linked-service/main.bicep' = [ parameters: linkedService.?parameters description: linkedService.?description } + dependsOn: [ + dataFactory_integrationRuntimes + ] } ] @@ -586,3 +587,54 @@ type customerManagedKeyType = { @description('Optional. User assigned identity to use when fetching the customer managed key. Required if no system assigned identity is available for use.') userAssignedIdentityResourceId: string? }? + +type managedPrivateEndpointType = { + @description('Required. Specify the name of managed private endpoint.') + name: string + + @description('Required. Specify the sub-resource of the managed private endpoint.') + groupId: string + + @description('Required. Specify the resource ID to create the managed private endpoint for.') + privateLinkResourceId: string + + @description('Optional. Specify the FQDNS of the linked resources to create private endpoints for, depending on the type of linked resource this is required.') + fqdns: string[]? +} + +type integrationRuntimesType = { + @description('Required. Specify the name of integration runtime.') + name: string + + @description('Required. Specify the type of the integration runtime.') + type: ('Managed' | 'SelfHosted') + + @description('Optional. Specify custom description for the integration runtime.') + integrationRuntimeCustomDescription: string? + + @description('Optional. Specify managed vritual network name for the integration runtime to link to.') + managedVirtualNetworkName: string? + + @description('Optional. Integration Runtime type properties. Required if type is "Managed".') + typeProperties: object? +}[] + +type linkedServicesType = { + @description('Required. The name of the Linked Service.') + name: string + + @description('Required. The type of Linked Service. See https://learn.microsoft.com/en-us/azure/templates/microsoft.datafactory/factories/linkedservices?pivots=deployment-language-bicep#linkedservice-objects for more information.') + type: string + + @description('Optional. Used to add connection properties for your linked services.') + typeProperties: object? + + @description('Optional. The name of the Integration Runtime to use.') + integrationRuntimeName: string? + + @description('Optional. Use this to add parameters for a linked service connection string.') + parameters: object? + + @description('Optional. The description of the Integration Runtime.') + description: string? +}[] diff --git a/avm/res/data-factory/factory/main.json b/avm/res/data-factory/factory/main.json index 310b27936e..5e8ffea4e0 100644 --- a/avm/res/data-factory/factory/main.json +++ b/avm/res/data-factory/factory/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "18076044993800210191" + "version": "0.30.23.60470", + "templateHash": "15687596888855868361" }, "name": "Data Factories", "description": "This module deploys a Data Factory.", @@ -505,6 +505,132 @@ } }, "nullable": true + }, + "managedPrivateEndpointType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Specify the name of managed private endpoint." + } + }, + "groupId": { + "type": "string", + "metadata": { + "description": "Required. Specify the sub-resource of the managed private endpoint." + } + }, + "privateLinkResourceId": { + "type": "string", + "metadata": { + "description": "Required. Specify the resource ID to create the managed private endpoint for." + } + }, + "fqdns": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Specify the FQDNS of the linked resources to create private endpoints for, depending on the type of linked resource this is required." + } + } + } + }, + "integrationRuntimesType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Specify the name of integration runtime." + } + }, + "type": { + "type": "string", + "allowedValues": [ + "Managed", + "SelfHosted" + ], + "metadata": { + "description": "Required. Specify the type of the integration runtime." + } + }, + "integrationRuntimeCustomDescription": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify custom description for the integration runtime." + } + }, + "managedVirtualNetworkName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify managed vritual network name for the integration runtime to link to." + } + }, + "typeProperties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Integration Runtime type properties. Required if type is \"Managed\"." + } + } + } + } + }, + "linkedServicesType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Linked Service." + } + }, + "type": { + "type": "string", + "metadata": { + "description": "Required. The type of Linked Service. See https://learn.microsoft.com/en-us/azure/templates/microsoft.datafactory/factories/linkedservices?pivots=deployment-language-bicep#linkedservice-objects for more information." + } + }, + "typeProperties": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Used to add connection properties for your linked services." + } + }, + "integrationRuntimeName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Integration Runtime to use." + } + }, + "parameters": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Use this to add parameters for a linked service connection string." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the Integration Runtime." + } + } + } + } } }, "parameters": { @@ -523,20 +649,23 @@ }, "managedPrivateEndpoints": { "type": "array", + "items": { + "$ref": "#/definitions/managedPrivateEndpointType" + }, "defaultValue": [], "metadata": { "description": "Optional. An array of managed private endpoints objects created in the Data Factory managed virtual network." } }, "integrationRuntimes": { - "type": "array", + "$ref": "#/definitions/integrationRuntimesType", "defaultValue": [], "metadata": { "description": "Optional. An array of objects for the configuration of an Integration Runtime." } }, "linkedServices": { - "type": "array", + "$ref": "#/definitions/linkedServicesType", "defaultValue": [], "metadata": { "description": "Optional. An array of objects for the configuration of Linked Services." @@ -888,8 +1017,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "17839923462414788715" + "version": "0.30.23.60470", + "templateHash": "1782105630855230474" }, "name": "Data Factory Managed Virtual Networks", "description": "This module deploys a Data Factory Managed Virtual Network.", @@ -962,8 +1091,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3694105160445163406" + "version": "0.30.23.60470", + "templateHash": "6720514642858120112" }, "name": "Data Factory Managed Virtual Network Managed PrivateEndpoints", "description": "This module deploys a Data Factory Managed Virtual Network Managed Private Endpoint.", @@ -1099,10 +1228,14 @@ "value": "[parameters('integrationRuntimes')[copyIndex()].type]" }, "integrationRuntimeCustomDescription": { - "value": "[coalesce(tryGet(parameters('integrationRuntimes')[copyIndex()], 'integrationRuntimeCustomDescription'), 'Managed Integration Runtime created by avm-res-datafactory-factories')]" + "value": "[tryGet(parameters('integrationRuntimes')[copyIndex()], 'integrationRuntimeCustomDescription')]" }, - "managedVirtualNetworkName": "[if(contains(parameters('integrationRuntimes')[copyIndex()], 'managedVirtualNetworkName'), createObject('value', parameters('integrationRuntimes')[copyIndex()].managedVirtualNetworkName), createObject('value', ''))]", - "typeProperties": "[if(contains(parameters('integrationRuntimes')[copyIndex()], 'typeProperties'), createObject('value', parameters('integrationRuntimes')[copyIndex()].typeProperties), createObject('value', createObject()))]" + "managedVirtualNetworkName": { + "value": "[tryGet(parameters('integrationRuntimes')[copyIndex()], 'managedVirtualNetworkName')]" + }, + "typeProperties": { + "value": "[tryGet(parameters('integrationRuntimes')[copyIndex()], 'typeProperties')]" + } }, "template": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", @@ -1110,8 +1243,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "7050455062079134223" + "version": "0.30.23.60470", + "templateHash": "17201448291064419524" }, "name": "Data Factory Integration RunTimes", "description": "This module deploys a Data Factory Managed or Self-Hosted Integration Runtime.", @@ -1248,8 +1381,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "2964661488202892260" + "version": "0.30.23.60470", + "templateHash": "5456106394920661740" }, "name": "Data Factory Linked Service", "description": "This module deploys a Data Factory Linked Service.", @@ -1344,7 +1477,8 @@ } }, "dependsOn": [ - "dataFactory" + "dataFactory", + "dataFactory_integrationRuntimes" ] }, "dataFactory_privateEndpoints": { diff --git a/avm/res/data-factory/factory/managed-virtual-network/main.json b/avm/res/data-factory/factory/managed-virtual-network/main.json index f84cc1629b..22a3f54962 100644 --- a/avm/res/data-factory/factory/managed-virtual-network/main.json +++ b/avm/res/data-factory/factory/managed-virtual-network/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "17839923462414788715" + "version": "0.30.23.60470", + "templateHash": "1782105630855230474" }, "name": "Data Factory Managed Virtual Networks", "description": "This module deploys a Data Factory Managed Virtual Network.", @@ -78,8 +78,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3694105160445163406" + "version": "0.30.23.60470", + "templateHash": "6720514642858120112" }, "name": "Data Factory Managed Virtual Network Managed PrivateEndpoints", "description": "This module deploys a Data Factory Managed Virtual Network Managed Private Endpoint.", diff --git a/avm/res/data-factory/factory/managed-virtual-network/managed-private-endpoint/main.json b/avm/res/data-factory/factory/managed-virtual-network/managed-private-endpoint/main.json index 28c3cf639b..c9c9e60513 100644 --- a/avm/res/data-factory/factory/managed-virtual-network/managed-private-endpoint/main.json +++ b/avm/res/data-factory/factory/managed-virtual-network/managed-private-endpoint/main.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "3694105160445163406" + "version": "0.30.23.60470", + "templateHash": "6720514642858120112" }, "name": "Data Factory Managed Virtual Network Managed PrivateEndpoints", "description": "This module deploys a Data Factory Managed Virtual Network Managed Private Endpoint.", diff --git a/avm/res/data-factory/factory/tests/e2e/max/main.test.bicep b/avm/res/data-factory/factory/tests/e2e/max/main.test.bicep index 03a1f38a74..a134efefe6 100644 --- a/avm/res/data-factory/factory/tests/e2e/max/main.test.bicep +++ b/avm/res/data-factory/factory/tests/e2e/max/main.test.bicep @@ -136,7 +136,7 @@ module testDeployment '../../../main.bicep' = [ } type: 'AzureBlobFS' typeProperties: { - url: '@{concat(\'https://\', linkedService().storageAccountName, \'.dfs.core.windows.net\')}' + url: '@{concat(\'https://\', linkedService().storageAccountName, \'.dfs.${environment().suffixes.storage}\')}' } } ] From 44c2f98d2259b42424707cb6a032a6c6ec3f47ea Mon Sep 17 00:00:00 2001 From: Kris Baranek <20225789+krbar@users.noreply.github.com> Date: Tue, 1 Oct 2024 16:50:52 +0200 Subject: [PATCH 53/57] fix: `prefixLength` minimum value in `avm/res/network/public-ip-prefix` (#3382) ## Description Updates the minimum value of the `prefixLength` parameter to `/21` as per: - https://learn.microsoft.com/en-us/azure/virtual-network/ip-services/public-ip-address-prefix > If you are [deriving a Public IP Prefix from a Custom IP Prefix (BYOIP range)](https://learn.microsoft.com/en-us/azure/virtual-network/ip-services/manage-custom-ip-address-prefix#create-a-public-ip-prefix-from-a-custom-ip-prefix), the prefix size can be as large as the Custom IP Prefix. - https://learn.microsoft.com/en-us/azure/virtual-network/ip-services/custom-ip-address-prefix : > A custom IPv4 Prefix must be between /21 and /24 Fixes #3367 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.network.public-ip-prefix](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-prefix.yml/badge.svg?branch=users%2Fkrbar%2FpfxFix)](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.network.public-ip-prefix.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [x] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/network/public-ip-prefix/main.bicep | 2 +- avm/res/network/public-ip-prefix/main.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/avm/res/network/public-ip-prefix/main.bicep b/avm/res/network/public-ip-prefix/main.bicep index ce7ae8941a..f53aaf8b6c 100644 --- a/avm/res/network/public-ip-prefix/main.bicep +++ b/avm/res/network/public-ip-prefix/main.bicep @@ -10,7 +10,7 @@ param name string param location string = resourceGroup().location @description('Required. Length of the Public IP Prefix.') -@minValue(28) +@minValue(21) @maxValue(31) param prefixLength int diff --git a/avm/res/network/public-ip-prefix/main.json b/avm/res/network/public-ip-prefix/main.json index f3c6be1419..31a4295388 100644 --- a/avm/res/network/public-ip-prefix/main.json +++ b/avm/res/network/public-ip-prefix/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.29.47.4906", - "templateHash": "2074867794511783977" + "version": "0.30.23.60470", + "templateHash": "13346619343009869073" }, "name": "Public IP Prefixes", "description": "This module deploys a Public IP Prefix.", @@ -129,7 +129,7 @@ }, "prefixLength": { "type": "int", - "minValue": 28, + "minValue": 21, "maxValue": 31, "metadata": { "description": "Required. Length of the Public IP Prefix." From 7a2ea326c105d14c8e9ebc1fa8d4b26ff2956a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenjie=20Yu=EF=BC=88MSFT=EF=BC=89?= <81678720+zedy-wj@users.noreply.github.com> Date: Wed, 2 Oct 2024 02:15:36 +0800 Subject: [PATCH 54/57] feat: Add new module `document-db/mongo-cluster` (#2790) ## Description Related PR: https://github.com/Azure/azure-dev/pull/3507 Add a new module `document-db/mongo-cluster` to support new feature. ## Pipeline Reference | Pipeline | | -------- | |[![avm.res.document-db.mongo-cluster](https://github.com/zedy-wj/bicep-registry-modules/actions/workflows/avm.res.document-db.mongo-cluster.yml/badge.svg?branch=document-db%2FmongoClusters)](https://github.com/zedy-wj/bicep-registry-modules/actions/workflows/avm.res.document-db.mongo-cluster.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings @jongio - for notification. --------- Co-authored-by: zedy Co-authored-by: Menghua Chen (WICRESOFT NORTH AMERICA LTD) Co-authored-by: Menghua Chen (MSFT) <111940661+Menghua1@users.noreply.github.com> Co-authored-by: Yohan Lasorsa --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + .../avm.res.document-db.mongo-cluster.yml | 88 + avm/res/document-db/mongo-cluster/README.md | 1393 ++++++++++++ .../mongo-cluster/firewall-rule/README.md | 67 + .../mongo-cluster/firewall-rule/main.bicep | 37 + .../mongo-cluster/firewall-rule/main.json | 74 + avm/res/document-db/mongo-cluster/main.bicep | 535 +++++ avm/res/document-db/mongo-cluster/main.json | 1898 +++++++++++++++++ .../modules/keyVaultExport.bicep | 50 + .../tests/e2e/defaults/main.test.bicep | 57 + .../tests/e2e/kvSecrets/dependencies.bicep | 21 + .../tests/e2e/kvSecrets/main.test.bicep | 72 + .../tests/e2e/max/dependencies.bicep | 102 + .../tests/e2e/max/main.test.bicep | 163 ++ .../tests/e2e/waf-aligned/main.test.bicep | 57 + .../document-db/mongo-cluster/version.json | 7 + 17 files changed, 4623 insertions(+) create mode 100644 .github/workflows/avm.res.document-db.mongo-cluster.yml create mode 100644 avm/res/document-db/mongo-cluster/README.md create mode 100644 avm/res/document-db/mongo-cluster/firewall-rule/README.md create mode 100644 avm/res/document-db/mongo-cluster/firewall-rule/main.bicep create mode 100644 avm/res/document-db/mongo-cluster/firewall-rule/main.json create mode 100644 avm/res/document-db/mongo-cluster/main.bicep create mode 100644 avm/res/document-db/mongo-cluster/main.json create mode 100644 avm/res/document-db/mongo-cluster/modules/keyVaultExport.bicep create mode 100644 avm/res/document-db/mongo-cluster/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/document-db/mongo-cluster/tests/e2e/kvSecrets/dependencies.bicep create mode 100644 avm/res/document-db/mongo-cluster/tests/e2e/kvSecrets/main.test.bicep create mode 100644 avm/res/document-db/mongo-cluster/tests/e2e/max/dependencies.bicep create mode 100644 avm/res/document-db/mongo-cluster/tests/e2e/max/main.test.bicep create mode 100644 avm/res/document-db/mongo-cluster/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/document-db/mongo-cluster/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index fff88d76bc..2e2123c29c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -66,6 +66,7 @@ /avm/res/dev-test-lab/lab/ @Azure/avm-res-devtestlab-lab-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/digital-twins/digital-twins-instance/ @Azure/avm-res-digitaltwins-digitaltwinsinstance-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/document-db/database-account/ @Azure/avm-res-documentdb-databaseaccount-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/res/document-db/mongo-cluster/ @Azure/avm-res-documentdb-mongocluster-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/event-grid/domain/ @Azure/avm-res-eventgrid-domain-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/event-grid/namespace/ @Azure/avm-res-eventgrid-namespace-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/event-grid/system-topic/ @Azure/avm-res-eventgrid-systemtopic-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index feded8d600..d9e7855858 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -101,6 +101,7 @@ body: - "avm/res/dev-test-lab/lab" - "avm/res/digital-twins/digital-twins-instance" - "avm/res/document-db/database-account" + - "avm/res/document-db/mongo-cluster" - "avm/res/event-grid/domain" - "avm/res/event-grid/namespace" - "avm/res/event-grid/system-topic" diff --git a/.github/workflows/avm.res.document-db.mongo-cluster.yml b/.github/workflows/avm.res.document-db.mongo-cluster.yml new file mode 100644 index 0000000000..f7b513ae03 --- /dev/null +++ b/.github/workflows/avm.res.document-db.mongo-cluster.yml @@ -0,0 +1,88 @@ +name: "avm.res.document-db.mongo-cluster" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.document-db.mongo-cluster.yml" + - "avm/res/document-db/mongo-cluster/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/document-db/mongo-cluster" + workflowPath: ".github/workflows/avm.res.document-db.mongo-cluster.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/document-db/mongo-cluster/README.md b/avm/res/document-db/mongo-cluster/README.md new file mode 100644 index 0000000000..c4af03e2b4 --- /dev/null +++ b/avm/res/document-db/mongo-cluster/README.md @@ -0,0 +1,1393 @@ +# Azure Cosmos DB MongoDB vCore cluster `[Microsoft.DocumentDB/mongoClusters]` + +This module deploys a Azure Cosmos DB MongoDB vCore cluster. + +**Note:** This module is not intended for broad, generic use, as it was designed to cater for the requirements of the AZD CLI product. Feature requests and bug fix requests are welcome if they support the development of the AZD CLI but may not be incorporated if they aim to make this module more generic than what it needs to be for its primary use case. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Cross-referenced modules](#Cross-referenced-modules) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.DocumentDB/mongoClusters` | [2024-02-15-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2024-02-15-preview/mongoClusters) | +| `Microsoft.DocumentDB/mongoClusters/firewallRules` | [2024-02-15-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2024-02-15-preview/mongoClusters/firewallRules) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.KeyVault/vaults/secrets` | [2023-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2023-07-01/vaults/secrets) | +| `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/document-db/mongo-cluster:`. + +- [Using only defaults](#example-1-using-only-defaults) +- [Deploying with a key vault reference to save secrets](#example-2-deploying-with-a-key-vault-reference-to-save-secrets) +- [Using large parameter set](#example-3-using-large-parameter-set) +- [WAF-aligned](#example-4-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module mongoCluster 'br/public:avm/res/document-db/mongo-cluster:' = { + name: 'mongoClusterDeployment' + params: { + // Required parameters + administratorLogin: 'Admin001' + administratorLoginPassword: '' + name: 'ddmcdefmin001' + nodeCount: 2 + sku: 'M30' + storage: 256 + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "administratorLogin": { + "value": "Admin001" + }, + "administratorLoginPassword": { + "value": "" + }, + "name": { + "value": "ddmcdefmin001" + }, + "nodeCount": { + "value": 2 + }, + "sku": { + "value": "M30" + }, + "storage": { + "value": 256 + }, + // Non-required parameters + "location": { + "value": "" + } + } +} +``` + +
+

+ +### Example 2: _Deploying with a key vault reference to save secrets_ + +This instance deploys the module saving its secrets in a key vault. + + +

+ +via Bicep module + +```bicep +module mongoCluster 'br/public:avm/res/document-db/mongo-cluster:' = { + name: 'mongoClusterDeployment' + params: { + // Required parameters + administratorLogin: 'Admin002' + administratorLoginPassword: '' + name: 'kv-ref' + nodeCount: 2 + sku: 'M30' + storage: 256 + // Non-required parameters + location: '' + secretsExportConfiguration: { + connectionStringSecretName: 'connectionString' + keyVaultResourceId: '' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "administratorLogin": { + "value": "Admin002" + }, + "administratorLoginPassword": { + "value": "" + }, + "name": { + "value": "kv-ref" + }, + "nodeCount": { + "value": 2 + }, + "sku": { + "value": "M30" + }, + "storage": { + "value": 256 + }, + // Non-required parameters + "location": { + "value": "" + }, + "secretsExportConfiguration": { + "value": { + "connectionStringSecretName": "connectionString", + "keyVaultResourceId": "" + } + } + } +} +``` + +
+

+ +### Example 3: _Using large parameter set_ + +This instance deploys the module with the maximum set of required parameters. + + +

+ +via Bicep module + +```bicep +module mongoCluster 'br/public:avm/res/document-db/mongo-cluster:' = { + name: 'mongoClusterDeployment' + params: { + // Required parameters + administratorLogin: 'Admin003' + administratorLoginPassword: '' + name: 'ddmcmax001' + nodeCount: 2 + sku: 'M30' + storage: 256 + // Non-required parameters + createMode: 'Default' + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + highAvailabilityMode: false + location: '' + networkAcls: { + allowAllIPs: true + allowAzureIPs: true + customRules: [ + { + endIpAddress: '5.6.7.8' + firewallRuleName: 'allow-1.2.3.4-to-5.6.7.8' + startIpAddress: '1.2.3.4' + } + ] + } + nodeType: 'Shard' + privateEndpoints: [ + { + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } + subnetResourceId: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } + { + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: '' + } + ] + } + subnetResourceId: '' + } + ] + roleAssignments: [ + { + name: '60395919-cfd3-47bf-8349-775ddebb255e' + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + { + name: '' + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' + } + ] + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "administratorLogin": { + "value": "Admin003" + }, + "administratorLoginPassword": { + "value": "" + }, + "name": { + "value": "ddmcmax001" + }, + "nodeCount": { + "value": 2 + }, + "sku": { + "value": "M30" + }, + "storage": { + "value": 256 + }, + // Non-required parameters + "createMode": { + "value": "Default" + }, + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ] + }, + "highAvailabilityMode": { + "value": false + }, + "location": { + "value": "" + }, + "networkAcls": { + "value": { + "allowAllIPs": true, + "allowAzureIPs": true, + "customRules": [ + { + "endIpAddress": "5.6.7.8", + "firewallRuleName": "allow-1.2.3.4-to-5.6.7.8", + "startIpAddress": "1.2.3.4" + } + ] + } + }, + "nodeType": { + "value": "Shard" + }, + "privateEndpoints": { + "value": [ + { + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, + "subnetResourceId": "", + "tags": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + }, + { + "privateDnsZoneGroup": { + "privateDnsZoneGroupConfigs": [ + { + "privateDnsZoneResourceId": "" + } + ] + }, + "subnetResourceId": "" + } + ] + }, + "roleAssignments": { + "value": [ + { + "name": "60395919-cfd3-47bf-8349-775ddebb255e", + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Owner" + }, + { + "name": "", + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" + } + ] + } + } +} +``` + +
+

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

+ +via Bicep module + +```bicep +module mongoCluster 'br/public:avm/res/document-db/mongo-cluster:' = { + name: 'mongoClusterDeployment' + params: { + // Required parameters + administratorLogin: 'Admin001' + administratorLoginPassword: '' + name: 'ddmcwaf001' + nodeCount: 2 + sku: 'M30' + storage: 256 + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "administratorLogin": { + "value": "Admin001" + }, + "administratorLoginPassword": { + "value": "" + }, + "name": { + "value": "ddmcwaf001" + }, + "nodeCount": { + "value": 2 + }, + "sku": { + "value": "M30" + }, + "storage": { + "value": 256 + }, + // Non-required parameters + "location": { + "value": "" + } + } +} +``` + +
+

+ +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`administratorLogin`](#parameter-administratorlogin) | string | Username for admin user. | +| [`administratorLoginPassword`](#parameter-administratorloginpassword) | securestring | Password for admin user. | +| [`name`](#parameter-name) | string | Name of the Azure Cosmos DB MongoDB vCore cluster. | +| [`nodeCount`](#parameter-nodecount) | int | Number of nodes in the node group. | +| [`sku`](#parameter-sku) | string | SKU defines the CPU and memory that is provisioned for each node. | +| [`storage`](#parameter-storage) | int | Disk storage size for the node group in GB. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`createMode`](#parameter-createmode) | string | Mode to create the azure cosmos db mongodb vCore cluster. | +| [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`highAvailabilityMode`](#parameter-highavailabilitymode) | bool | Whether high availability is enabled on the node group. | +| [`location`](#parameter-location) | string | Default to current resource group scope location. Location for all resources. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`networkAcls`](#parameter-networkacls) | object | IP addresses to allow access to the cluster from. | +| [`nodeType`](#parameter-nodetype) | string | Deployed Node type in the node group. | +| [`privateEndpoints`](#parameter-privateendpoints) | array | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`secretsExportConfiguration`](#parameter-secretsexportconfiguration) | object | Key vault reference and secret settings for the module's secrets export. | +| [`tags`](#parameter-tags) | object | Tags of the Database Account resource. | + +### Parameter: `administratorLogin` + +Username for admin user. + +- Required: Yes +- Type: string + +### Parameter: `administratorLoginPassword` + +Password for admin user. + +- Required: Yes +- Type: securestring + +### Parameter: `name` + +Name of the Azure Cosmos DB MongoDB vCore cluster. + +- Required: Yes +- Type: string + +### Parameter: `nodeCount` + +Number of nodes in the node group. + +- Required: Yes +- Type: int + +### Parameter: `sku` + +SKU defines the CPU and memory that is provisioned for each node. + +- Required: Yes +- Type: string + +### Parameter: `storage` + +Disk storage size for the node group in GB. + +- Required: Yes +- Type: int + +### Parameter: `createMode` + +Mode to create the azure cosmos db mongodb vCore cluster. + +- Required: No +- Type: string +- Default: `'Default'` + +### Parameter: `diagnosticSettings` + +The diagnostic settings of the service. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| [`eventHubName`](#parameter-diagnosticsettingseventhubname) | string | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | +| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. | +| [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | +| [`metricCategories`](#parameter-diagnosticsettingsmetriccategories) | array | The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. | +| [`name`](#parameter-diagnosticsettingsname) | string | The name of diagnostic setting. | +| [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | + +### Parameter: `diagnosticSettings.eventHubAuthorizationRuleResourceId` + +Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.eventHubName` + +Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `diagnosticSettings.logCategoriesAndGroups` + +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. | +| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. | +| [`enabled`](#parameter-diagnosticsettingslogcategoriesandgroupsenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.category` + +Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.categoryGroup` + +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.metricCategories` + +The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingsmetriccategoriescategory) | string | Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enabled`](#parameter-diagnosticsettingsmetriccategoriesenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.metricCategories.category` + +Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. + +- Required: Yes +- Type: string + +### Parameter: `diagnosticSettings.metricCategories.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.name` + +The name of diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.storageAccountResourceId` + +Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.workspaceResourceId` + +Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `highAvailabilityMode` + +Whether high availability is enabled on the node group. + +- Required: No +- Type: bool +- Default: `False` + +### Parameter: `location` + +Default to current resource group scope location. Location for all resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `networkAcls` + +IP addresses to allow access to the cluster from. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`allowAllIPs`](#parameter-networkaclsallowallips) | bool | Indicates whether to allow all IP addresses. | +| [`allowAzureIPs`](#parameter-networkaclsallowazureips) | bool | Indicates whether to allow all Azure internal IP addresses. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`customRules`](#parameter-networkaclscustomrules) | array | List of custom firewall rules. | + +### Parameter: `networkAcls.allowAllIPs` + +Indicates whether to allow all IP addresses. + +- Required: Yes +- Type: bool + +### Parameter: `networkAcls.allowAzureIPs` + +Indicates whether to allow all Azure internal IP addresses. + +- Required: Yes +- Type: bool + +### Parameter: `networkAcls.customRules` + +List of custom firewall rules. + +- Required: No +- Type: array + +### Parameter: `nodeType` + +Deployed Node type in the node group. + +- Required: No +- Type: string +- Default: `'Shard'` + +### Parameter: `privateEndpoints` + +Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`subnetResourceId`](#parameter-privateendpointssubnetresourceid) | string | Resource ID of the subnet where the endpoint needs to be created. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`applicationSecurityGroupResourceIds`](#parameter-privateendpointsapplicationsecuritygroupresourceids) | array | Application security groups in which the private endpoint IP configuration is included. | +| [`customDnsConfigs`](#parameter-privateendpointscustomdnsconfigs) | array | Custom DNS configurations. | +| [`customNetworkInterfaceName`](#parameter-privateendpointscustomnetworkinterfacename) | string | The custom name of the network interface attached to the private endpoint. | +| [`enableTelemetry`](#parameter-privateendpointsenabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`ipConfigurations`](#parameter-privateendpointsipconfigurations) | array | A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. | +| [`isManualConnection`](#parameter-privateendpointsismanualconnection) | bool | If Manual Private Link Connection is required. | +| [`location`](#parameter-privateendpointslocation) | string | The location to deploy the private endpoint to. | +| [`lock`](#parameter-privateendpointslock) | object | Specify the type of lock. | +| [`manualConnectionRequestMessage`](#parameter-privateendpointsmanualconnectionrequestmessage) | string | A message passed to the owner of the remote resource with the manual connection request. | +| [`name`](#parameter-privateendpointsname) | string | The name of the private endpoint. | +| [`privateDnsZoneGroup`](#parameter-privateendpointsprivatednszonegroup) | object | The private DNS zone group to configure for the private endpoint. | +| [`privateLinkServiceConnectionName`](#parameter-privateendpointsprivatelinkserviceconnectionname) | string | The name of the private link connection to create. | +| [`resourceGroupName`](#parameter-privateendpointsresourcegroupname) | string | Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. | +| [`roleAssignments`](#parameter-privateendpointsroleassignments) | array | Array of role assignments to create. | +| [`service`](#parameter-privateendpointsservice) | string | The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". | +| [`tags`](#parameter-privateendpointstags) | object | Tags to be applied on all resources/resource groups in this deployment. | + +### Parameter: `privateEndpoints.subnetResourceId` + +Resource ID of the subnet where the endpoint needs to be created. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.applicationSecurityGroupResourceIds` + +Application security groups in which the private endpoint IP configuration is included. + +- Required: No +- Type: array + +### Parameter: `privateEndpoints.customDnsConfigs` + +Custom DNS configurations. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`fqdn`](#parameter-privateendpointscustomdnsconfigsfqdn) | string | Fqdn that resolves to private endpoint ip address. | +| [`ipAddresses`](#parameter-privateendpointscustomdnsconfigsipaddresses) | array | A list of private ip addresses of the private endpoint. | + +### Parameter: `privateEndpoints.customDnsConfigs.fqdn` + +Fqdn that resolves to private endpoint ip address. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.customDnsConfigs.ipAddresses` + +A list of private ip addresses of the private endpoint. + +- Required: Yes +- Type: array + +### Parameter: `privateEndpoints.customNetworkInterfaceName` + +The custom name of the network interface attached to the private endpoint. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool + +### Parameter: `privateEndpoints.ipConfigurations` + +A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsipconfigurationsname) | string | The name of the resource that is unique within a resource group. | +| [`properties`](#parameter-privateendpointsipconfigurationsproperties) | object | Properties of private endpoint IP configurations. | + +### Parameter: `privateEndpoints.ipConfigurations.name` + +The name of the resource that is unique within a resource group. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.ipConfigurations.properties` + +Properties of private endpoint IP configurations. + +- Required: Yes +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`groupId`](#parameter-privateendpointsipconfigurationspropertiesgroupid) | string | The ID of a group obtained from the remote resource that this private endpoint should connect to. | +| [`memberName`](#parameter-privateendpointsipconfigurationspropertiesmembername) | string | The member name of a group obtained from the remote resource that this private endpoint should connect to. | +| [`privateIPAddress`](#parameter-privateendpointsipconfigurationspropertiesprivateipaddress) | string | A private ip address obtained from the private endpoint's subnet. | + +### Parameter: `privateEndpoints.ipConfigurations.properties.groupId` + +The ID of a group obtained from the remote resource that this private endpoint should connect to. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.ipConfigurations.properties.memberName` + +The member name of a group obtained from the remote resource that this private endpoint should connect to. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.ipConfigurations.properties.privateIPAddress` + +A private ip address obtained from the private endpoint's subnet. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.isManualConnection` + +If Manual Private Link Connection is required. + +- Required: No +- Type: bool + +### Parameter: `privateEndpoints.location` + +The location to deploy the private endpoint to. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.lock` + +Specify the type of lock. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-privateendpointslockkind) | string | Specify the type of lock. | +| [`name`](#parameter-privateendpointslockname) | string | Specify the name of lock. | + +### Parameter: `privateEndpoints.lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `privateEndpoints.lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.manualConnectionRequestMessage` + +A message passed to the owner of the remote resource with the manual connection request. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.name` + +The name of the private endpoint. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup` + +The private DNS zone group to configure for the private endpoint. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneGroupConfigs`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigs) | array | The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupname) | string | The name of the Private DNS Zone Group. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs` + +The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`privateDnsZoneResourceId`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsprivatednszoneresourceid) | string | The resource id of the private DNS zone. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-privateendpointsprivatednszonegroupprivatednszonegroupconfigsname) | string | The name of the private DNS zone group config. | + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.privateDnsZoneResourceId` + +The resource id of the private DNS zone. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.privateDnsZoneGroupConfigs.name` + +The name of the private DNS zone group config. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.privateDnsZoneGroup.name` + +The name of the Private DNS Zone Group. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.privateLinkServiceConnectionName` + +The name of the private link connection to create. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.resourceGroupName` + +Specify if you want to deploy the Private Endpoint into a different resource group than the main resource. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array +- Roles configurable by name: + - `'Contributor'` + - `'DNS Resolver Contributor'` + - `'DNS Zone Contributor'` + - `'Domain Services Contributor'` + - `'Domain Services Reader'` + - `'Network Contributor'` + - `'Owner'` + - `'Private DNS Zone Contributor'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-privateendpointsroleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-privateendpointsroleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-privateendpointsroleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". | +| [`conditionVersion`](#parameter-privateendpointsroleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-privateendpointsroleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-privateendpointsroleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-privateendpointsroleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | +| [`principalType`](#parameter-privateendpointsroleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `privateEndpoints.roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.condition` + +The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `privateEndpoints.roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `privateEndpoints.service` + +The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory". + +- Required: No +- Type: string + +### Parameter: `privateEndpoints.tags` + +Tags to be applied on all resources/resource groups in this deployment. + +- Required: No +- Type: object + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator (Preview)'` + - `'User Access Administrator'` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". | +| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `secretsExportConfiguration` + +Key vault reference and secret settings for the module's secrets export. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`keyVaultResourceId`](#parameter-secretsexportconfigurationkeyvaultresourceid) | string | The resource ID of the key vault where to store the secrets of this module. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`connectionStringSecretName`](#parameter-secretsexportconfigurationconnectionstringsecretname) | string | The name to use when creating the primary write connection string secret. | + +### Parameter: `secretsExportConfiguration.keyVaultResourceId` + +The resource ID of the key vault where to store the secrets of this module. + +- Required: Yes +- Type: string + +### Parameter: `secretsExportConfiguration.connectionStringSecretName` + +The name to use when creating the primary write connection string secret. + +- Required: No +- Type: string + +### Parameter: `tags` + +Tags of the Database Account resource. + +- Required: No +- Type: object + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `connectionStringKey` | string | The connection string key of the mongo cluster. | +| `exportedSecrets` | | The references to the secrets exported to the provided Key Vault. | +| `firewallRules` | array | The name and resource ID of firewall rule. | +| `mongoClusterResourceId` | string | The resource ID of the Azure Cosmos DB MongoDB vCore cluster. | +| `name` | string | The name of the Azure Cosmos DB MongoDB vCore cluster. | +| `privateEndpoints` | array | The private endpoints of the database account. | +| `resourceGroupName` | string | The name of the resource group the firewall rule was created in. | +| `resourceId` | string | The resource ID of the resource group the firewall rule was created in. | + +## Cross-referenced modules + +This section gives you an overview of all local-referenced module files (i.e., other modules that are referenced in this module) and all remote-referenced files (i.e., Bicep modules that are referenced from a Bicep Registry or Template Specs). + +| Reference | Type | +| :-- | :-- | +| `br/public:avm/res/network/private-endpoint:0.7.1` | Remote reference | + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/document-db/mongo-cluster/firewall-rule/README.md b/avm/res/document-db/mongo-cluster/firewall-rule/README.md new file mode 100644 index 0000000000..c60b28b7db --- /dev/null +++ b/avm/res/document-db/mongo-cluster/firewall-rule/README.md @@ -0,0 +1,67 @@ +# Azure Cosmos DB MongoDB vCore Cluster Config FireWall Rules `[Microsoft.DocumentDB/mongoClusters/firewallRules]` + +This module config firewall rules for the Azure Cosmos DB MongoDB vCore cluster. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.DocumentDB/mongoClusters/firewallRules` | [2024-02-15-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.DocumentDB/2024-02-15-preview/mongoClusters/firewallRules) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`endIpAddress`](#parameter-endipaddress) | string | The end IP address of the Azure Cosmos DB MongoDB vCore cluster firewall rule. Must be IPv4 format. | +| [`name`](#parameter-name) | string | The name of the firewall rule. | +| [`startIpAddress`](#parameter-startipaddress) | string | The start IP address of the Azure Cosmos DB MongoDB vCore cluster firewall rule. Must be IPv4 format. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`mongoClusterName`](#parameter-mongoclustername) | string | The name of the parent Azure Cosmos DB MongoDB vCore cluster. Required if the template is used in a standalone deployment. | + +### Parameter: `endIpAddress` + +The end IP address of the Azure Cosmos DB MongoDB vCore cluster firewall rule. Must be IPv4 format. + +- Required: Yes +- Type: string + +### Parameter: `name` + +The name of the firewall rule. + +- Required: Yes +- Type: string + +### Parameter: `startIpAddress` + +The start IP address of the Azure Cosmos DB MongoDB vCore cluster firewall rule. Must be IPv4 format. + +- Required: Yes +- Type: string + +### Parameter: `mongoClusterName` + +The name of the parent Azure Cosmos DB MongoDB vCore cluster. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the firewall rule. | +| `resourceGroupName` | string | The name of the resource group the Azure Cosmos DB MongoDB vCore cluster was created in. | +| `resourceId` | string | The resource ID of the firewall rule. | diff --git a/avm/res/document-db/mongo-cluster/firewall-rule/main.bicep b/avm/res/document-db/mongo-cluster/firewall-rule/main.bicep new file mode 100644 index 0000000000..38ea336d6b --- /dev/null +++ b/avm/res/document-db/mongo-cluster/firewall-rule/main.bicep @@ -0,0 +1,37 @@ +metadata name = 'Azure Cosmos DB MongoDB vCore Cluster Config FireWall Rules' +metadata description = 'This module config firewall rules for the Azure Cosmos DB MongoDB vCore cluster.' +metadata owner = 'Azure/module-maintainers' + +@description('Conditional. The name of the parent Azure Cosmos DB MongoDB vCore cluster. Required if the template is used in a standalone deployment.') +param mongoClusterName string + +@description('Required. The name of the firewall rule.') +param name string + +@description('Required. The start IP address of the Azure Cosmos DB MongoDB vCore cluster firewall rule. Must be IPv4 format.') +param startIpAddress string + +@description('Required. The end IP address of the Azure Cosmos DB MongoDB vCore cluster firewall rule. Must be IPv4 format.') +param endIpAddress string + +resource mongoCluster 'Microsoft.DocumentDB/mongoClusters@2024-02-15-preview' existing = { + name: mongoClusterName +} + +resource firewallRule 'Microsoft.DocumentDB/mongoClusters/firewallRules@2024-02-15-preview' = { + name: name + parent: mongoCluster + properties: { + startIpAddress: startIpAddress + endIpAddress: endIpAddress + } +} + +@description('The name of the resource group the Azure Cosmos DB MongoDB vCore cluster was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The name of the firewall rule.') +output name string = firewallRule.name + +@description('The resource ID of the firewall rule.') +output resourceId string = firewallRule.id diff --git a/avm/res/document-db/mongo-cluster/firewall-rule/main.json b/avm/res/document-db/mongo-cluster/firewall-rule/main.json new file mode 100644 index 0000000000..227ec11c2e --- /dev/null +++ b/avm/res/document-db/mongo-cluster/firewall-rule/main.json @@ -0,0 +1,74 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "11076682219298980277" + }, + "name": "Azure Cosmos DB MongoDB vCore Cluster Config FireWall Rules", + "description": "This module config firewall rules for the Azure Cosmos DB MongoDB vCore cluster.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "mongoClusterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Azure Cosmos DB MongoDB vCore cluster. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the firewall rule." + } + }, + "startIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The start IP address of the Azure Cosmos DB MongoDB vCore cluster firewall rule. Must be IPv4 format." + } + }, + "endIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The end IP address of the Azure Cosmos DB MongoDB vCore cluster firewall rule. Must be IPv4 format." + } + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/mongoClusters/firewallRules", + "apiVersion": "2024-02-15-preview", + "name": "[format('{0}/{1}', parameters('mongoClusterName'), parameters('name'))]", + "properties": { + "startIpAddress": "[parameters('startIpAddress')]", + "endIpAddress": "[parameters('endIpAddress')]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the Azure Cosmos DB MongoDB vCore cluster was created in." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the firewall rule." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the firewall rule." + }, + "value": "[resourceId('Microsoft.DocumentDB/mongoClusters/firewallRules', parameters('mongoClusterName'), parameters('name'))]" + } + } +} \ No newline at end of file diff --git a/avm/res/document-db/mongo-cluster/main.bicep b/avm/res/document-db/mongo-cluster/main.bicep new file mode 100644 index 0000000000..d4612a2a44 --- /dev/null +++ b/avm/res/document-db/mongo-cluster/main.bicep @@ -0,0 +1,535 @@ +metadata name = 'Azure Cosmos DB MongoDB vCore cluster' +metadata description = '''This module deploys a Azure Cosmos DB MongoDB vCore cluster. + +**Note:** This module is not intended for broad, generic use, as it was designed to cater for the requirements of the AZD CLI product. Feature requests and bug fix requests are welcome if they support the development of the AZD CLI but may not be incorporated if they aim to make this module more generic than what it needs to be for its primary use case.''' +metadata owner = 'Azure/module-maintainers' + +@description('Required. Name of the Azure Cosmos DB MongoDB vCore cluster.') +param name string + +@description('Optional. Default to current resource group scope location. Location for all resources.') +param location string = resourceGroup().location + +@description('Optional. Tags of the Database Account resource.') +param tags object? + +@description('Required. Username for admin user.') +param administratorLogin string + +@secure() +@description('Required. Password for admin user.') +@minLength(8) +@maxLength(128) +param administratorLoginPassword string + +@description('Optional. Mode to create the azure cosmos db mongodb vCore cluster.') +param createMode string = 'Default' + +@description('Optional. The diagnostic settings of the service.') +param diagnosticSettings diagnosticSettingType + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Optional. Whether high availability is enabled on the node group.') +param highAvailabilityMode bool = false + +@description('Optional. The lock settings of the service.') +param lock lockType + +@description('Optional. IP addresses to allow access to the cluster from.') +param networkAcls networkAclsType? + +@description('Required. Number of nodes in the node group.') +param nodeCount int + +@description('Optional. Deployed Node type in the node group.') +param nodeType string = 'Shard' + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints privateEndpointType + +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType + +@description('Optional. Key vault reference and secret settings for the module\'s secrets export.') +param secretsExportConfiguration secretsExportConfigurationType? + +@description('Required. SKU defines the CPU and memory that is provisioned for each node.') +param sku string + +@description('Required. Disk storage size for the node group in GB.') +param storage int + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator (Preview)': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) +} + +var firewallRules = union( + map(networkAcls.?customRules ?? [], customRule => { + name: customRule.?firewallRuleName ?? 'allow-${replace(customRule.startIpAddress, '.', '')}-to-${replace(customRule.endIpAddress, '.', '')}' + startIpAddress: customRule.startIpAddress + endIpAddress: customRule.endIpAddress + }), + networkAcls.?allowAllIPs ?? false + ? [ + { + name: 'allow-all-IPs' + startIpAddress: '0.0.0.0' + endIpAddress: '255.255.255.255' + } + ] + : [], + networkAcls.?allowAzureIPs ?? false + ? [ + { + name: 'allow-all-azure-internal-IPs' + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + ] + : [] +) + +var formattedRoleAssignments = [ + for (roleAssignment, index) in (roleAssignments ?? []): union(roleAssignment, { + roleDefinitionId: builtInRoleNames[?roleAssignment.roleDefinitionIdOrName] ?? (contains( + roleAssignment.roleDefinitionIdOrName, + '/providers/Microsoft.Authorization/roleDefinitions/' + ) + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName)) + }) +] + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { + name: '46d3xbcp.res.documentdb-mongocluster.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } +} + +resource mongoCluster 'Microsoft.DocumentDB/mongoClusters@2024-02-15-preview' = { + name: name + tags: tags + location: location + properties: { + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + createMode: createMode + nodeGroupSpecs: [ + { + diskSizeGB: storage + enableHa: highAvailabilityMode + kind: nodeType + nodeCount: nodeCount + sku: sku + } + ] + } +} + +resource mongoCluster_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ + for (diagnosticSetting, index) in (diagnosticSettings ?? []): { + name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' + properties: { + storageAccountId: diagnosticSetting.?storageAccountResourceId + workspaceId: diagnosticSetting.?workspaceResourceId + eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId + eventHubName: diagnosticSetting.?eventHubName + metrics: [ + for group in (diagnosticSetting.?metricCategories ?? [{ category: 'AllMetrics' }]): { + category: group.category + enabled: group.?enabled ?? true + timeGrain: null + } + ] + logs: [ + for group in (diagnosticSetting.?logCategoriesAndGroups ?? [{ categoryGroup: 'allLogs' }]): { + categoryGroup: group.?categoryGroup + category: group.?category + enabled: group.?enabled ?? true + } + ] + marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId + logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType + } + scope: mongoCluster + } +] + +resource mongoCluster_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid(mongoCluster.id, roleAssignment.principalId, roleAssignment.roleDefinitionId) + properties: { + roleDefinitionId: roleAssignment.roleDefinitionId + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: mongoCluster + } +] + +module mongoCluster_configFireWallRules 'firewall-rule/main.bicep' = [ + for (firewallRule, index) in firewallRules: { + name: '${uniqueString(deployment().name, location)}-firewallRule-${index}' + params: { + mongoClusterName: mongoCluster.name + name: firewallRule.name + startIpAddress: firewallRule.startIpAddress + endIpAddress: firewallRule.endIpAddress + } + } +] + +module secretsExport 'modules/keyVaultExport.bicep' = if (secretsExportConfiguration != null) { + name: '${uniqueString(deployment().name, location)}-secrets-kv' + scope: resourceGroup( + split((secretsExportConfiguration.?keyVaultResourceId ?? '//'), '/')[2], + split((secretsExportConfiguration.?keyVaultResourceId ?? '////'), '/')[4] + ) + params: { + keyVaultName: last(split(secretsExportConfiguration.?keyVaultResourceId ?? '//', '/')) + secretsToSet: union( + [], + contains(secretsExportConfiguration!, 'connectionStringSecretName') + ? [ + { + name: secretsExportConfiguration!.connectionStringSecretName + value: mongoCluster.properties.connectionString + } + ] + : [] + ) + } +} + +module mongoCluster_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.7.1' = [ + for (privateEndpoint, index) in (privateEndpoints ?? []): { + name: '${uniqueString(deployment().name, location)}-databaseAccount-PrivateEndpoint-${index}' + scope: resourceGroup(privateEndpoint.?resourceGroupName ?? '') + params: { + name: privateEndpoint.?name ?? 'pep-${last(split(mongoCluster.id, '/'))}-${privateEndpoint.?service ?? 'mongoCluster'}-${index}' + privateLinkServiceConnections: privateEndpoint.?isManualConnection != true + ? [ + { + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(mongoCluster.id, '/'))}-${privateEndpoint.?service ?? 'mongoCluster'}-${index}' + properties: { + privateLinkServiceId: mongoCluster.id + groupIds: [ + privateEndpoint.?service ?? 'mongoCluster' + ] + } + } + ] + : null + manualPrivateLinkServiceConnections: privateEndpoint.?isManualConnection == true + ? [ + { + name: privateEndpoint.?privateLinkServiceConnectionName ?? '${last(split(mongoCluster.id, '/'))}-${privateEndpoint.?service ?? 'mongoCluster'}-${index}' + properties: { + privateLinkServiceId: mongoCluster.id + groupIds: [ + privateEndpoint.?service ?? 'mongoCluster' + ] + requestMessage: privateEndpoint.?manualConnectionRequestMessage ?? 'Manual approval required.' + } + } + ] + : null + subnetResourceId: privateEndpoint.subnetResourceId + enableTelemetry: privateEndpoint.?enableTelemetry ?? enableTelemetry + location: privateEndpoint.?location ?? reference( + split(privateEndpoint.subnetResourceId, '/subnets/')[0], + '2020-06-01', + 'Full' + ).location + lock: privateEndpoint.?lock ?? lock + privateDnsZoneGroup: privateEndpoint.?privateDnsZoneGroup + roleAssignments: privateEndpoint.?roleAssignments + tags: privateEndpoint.?tags ?? tags + customDnsConfigs: privateEndpoint.?customDnsConfigs + ipConfigurations: privateEndpoint.?ipConfigurations + applicationSecurityGroupResourceIds: privateEndpoint.?applicationSecurityGroupResourceIds + customNetworkInterfaceName: privateEndpoint.?customNetworkInterfaceName + } + } +] + +@description('The name of the Azure Cosmos DB MongoDB vCore cluster.') +output name string = mongoCluster.name + +@description('The resource ID of the Azure Cosmos DB MongoDB vCore cluster.') +output mongoClusterResourceId string = mongoCluster.id + +@description('The resource ID of the resource group the firewall rule was created in.') +output resourceId string = resourceGroup().id + +@description('The name of the resource group the firewall rule was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The connection string key of the mongo cluster.') +output connectionStringKey string = mongoCluster.properties.connectionString + +@description('The name and resource ID of firewall rule.') +output firewallRules firewallSetType[] = [ + for index in range(0, length(firewallRules ?? [])): { + name: mongoCluster_configFireWallRules[index].outputs.name + resourceId: mongoCluster_configFireWallRules[index].outputs.resourceId + } +] + +@description('The private endpoints of the database account.') +output privateEndpoints array = [ + for (pe, i) in (!empty(privateEndpoints) ? array(privateEndpoints) : []): { + name: mongoCluster_privateEndpoints[i].outputs.name + resourceId: mongoCluster_privateEndpoints[i].outputs.resourceId + groupId: mongoCluster_privateEndpoints[i].outputs.groupId + customDnsConfig: mongoCluster_privateEndpoints[i].outputs.customDnsConfig + networkInterfaceIds: mongoCluster_privateEndpoints[i].outputs.networkInterfaceIds + } +] + +@description('The references to the secrets exported to the provided Key Vault.') +output exportedSecrets secretsOutputType = (secretsExportConfiguration != null) + ? toObject(secretsExport.outputs.secretsSet, secret => last(split(secret.secretResourceId, '/')), secret => secret) + : {} + +// =============== // +// Definitions // +// =============== // + +type diagnosticSettingType = { + @description('Optional. The name of diagnostic setting.') + name: string? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection.') + logCategoriesAndGroups: { + @description('Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.') + category: string? + + @description('Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs.') + categoryGroup: string? + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @description('Optional. The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection.') + metricCategories: { + @description('Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics.') + category: string + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @description('Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type.') + logAnalyticsDestinationType: ('Dedicated' | 'AzureDiagnostics')? + + @description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + workspaceResourceId: string? + + @description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + storageAccountResourceId: string? + + @description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') + eventHubAuthorizationRuleResourceId: string? + + @description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + eventHubName: string? + + @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') + marketplacePartnerResourceId: string? +}[]? + +type firewallSetType = { + @description('The name of the created firewall rule.') + name: string + + @description('The resource ID of the created firewall rule.') + resourceId: string +} + +type lockType = { + @description('Optional. Specify the name of lock.') + name: string? + + @description('Optional. Specify the type of lock.') + kind: ('CanNotDelete' | 'ReadOnly' | 'None')? +}? + +type networkAclsType = { + @description('Optional. List of custom firewall rules.') + customRules: [ + { + @description('Optional. The name of the custom firewall rule.') + firewallRuleName: string? + + @description('Required. The starting IP address for the custom firewall rule.') + startIpAddress: string + + @description('Required. The ending IP address for the custom firewall rule.') + endIpAddress: string + } + ]? + + @description('Required. Indicates whether to allow all IP addresses.') + allowAllIPs: bool + + @description('Required. Indicates whether to allow all Azure internal IP addresses.') + allowAzureIPs: bool +} + +type privateEndpointType = { + @description('Optional. The name of the private endpoint.') + name: string? + + @description('Optional. The location to deploy the private endpoint to.') + location: string? + + @description('Optional. The name of the private link connection to create.') + privateLinkServiceConnectionName: string? + + @description('Optional. The subresource to deploy the private endpoint for. For example "vault", "mysqlServer" or "dataFactory".') + service: string? + + @description('Required. Resource ID of the subnet where the endpoint needs to be created.') + subnetResourceId: string + + @description('Optional. The private DNS zone group to configure for the private endpoint.') + privateDnsZoneGroup: { + @description('Optional. The name of the Private DNS Zone Group.') + name: string? + + @description('Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones.') + privateDnsZoneGroupConfigs: { + @description('Optional. The name of the private DNS zone group config.') + name: string? + + @description('Required. The resource id of the private DNS zone.') + privateDnsZoneResourceId: string + }[] + }? + + @description('Optional. If Manual Private Link Connection is required.') + isManualConnection: bool? + + @description('Optional. A message passed to the owner of the remote resource with the manual connection request.') + @maxLength(140) + manualConnectionRequestMessage: string? + + @description('Optional. Custom DNS configurations.') + customDnsConfigs: { + @description('Required. Fqdn that resolves to private endpoint ip address.') + fqdn: string? + + @description('Required. A list of private ip addresses of the private endpoint.') + ipAddresses: string[] + }[]? + + @description('Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints.') + ipConfigurations: { + @description('Required. The name of the resource that is unique within a resource group.') + name: string + + @description('Required. Properties of private endpoint IP configurations.') + properties: { + @description('Required. The ID of a group obtained from the remote resource that this private endpoint should connect to.') + groupId: string + + @description('Required. The member name of a group obtained from the remote resource that this private endpoint should connect to.') + memberName: string + + @description('Required. A private ip address obtained from the private endpoint\'s subnet.') + privateIPAddress: string + } + }[]? + + @description('Optional. Application security groups in which the private endpoint IP configuration is included.') + applicationSecurityGroupResourceIds: string[]? + + @description('Optional. The custom name of the network interface attached to the private endpoint.') + customNetworkInterfaceName: string? + + @description('Optional. Specify the type of lock.') + lock: lockType + + @description('Optional. Array of role assignments to create.') + roleAssignments: roleAssignmentType + + @description('Optional. Tags to be applied on all resources/resource groups in this deployment.') + tags: object? + + @description('Optional. Enable/Disable usage telemetry for module.') + enableTelemetry: bool? + + @description('Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource.') + resourceGroupName: string? +}[]? + +type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') + roleDefinitionIdOrName: string + + @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') + principalId: string + + @description('Optional. The principal type of the assigned principal ID.') + principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')? + + @description('Optional. The description of the role assignment.') + description: string? + + @description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') + condition: string? + + @description('Optional. Version of the condition.') + conditionVersion: '2.0'? + + @description('Optional. The Resource Id of the delegated managed identity resource.') + delegatedManagedIdentityResourceId: string? +}[]? + +type secretsExportConfigurationType = { + @description('Required. The resource ID of the key vault where to store the secrets of this module.') + keyVaultResourceId: string + + @description('Optional. The name to use when creating the primary write connection string secret.') + connectionStringSecretName: string? +} + +import { secretSetType } from 'modules/keyVaultExport.bicep' +type secretsOutputType = { + @description('An exported secret\'s references.') + *: secretSetType +} diff --git a/avm/res/document-db/mongo-cluster/main.json b/avm/res/document-db/mongo-cluster/main.json new file mode 100644 index 0000000000..9d963be7d0 --- /dev/null +++ b/avm/res/document-db/mongo-cluster/main.json @@ -0,0 +1,1898 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "289094531739005234" + }, + "name": "Azure Cosmos DB MongoDB vCore cluster", + "description": "This module deploys a Azure Cosmos DB MongoDB vCore cluster.\n\n**Note:** This module is not intended for broad, generic use, as it was designed to cater for the requirements of the AZD CLI product. Feature requests and bug fix requests are welcome if they support the development of the AZD CLI but may not be incorporated if they aim to make this module more generic than what it needs to be for its primary use case.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + }, + "firewallSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the created firewall rule." + } + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the created firewall rule." + } + } + } + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "networkAclsType": { + "type": "object", + "properties": { + "customRules": { + "type": "array", + "prefixItems": [ + { + "type": "object", + "properties": { + "firewallRuleName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the custom firewall rule." + } + }, + "startIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The starting IP address for the custom firewall rule." + } + }, + "endIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The ending IP address for the custom firewall rule." + } + } + } + } + ], + "items": false, + "nullable": true, + "metadata": { + "description": "Optional. List of custom firewall rules." + } + }, + "allowAllIPs": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether to allow all IP addresses." + } + }, + "allowAzureIPs": { + "type": "bool", + "metadata": { + "description": "Required. Indicates whether to allow all Azure internal IP addresses." + } + } + } + }, + "privateEndpointType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private endpoint." + } + }, + "location": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The location to deploy the private endpoint to." + } + }, + "privateLinkServiceConnectionName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private link connection to create." + } + }, + "service": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The subresource to deploy the private endpoint for. For example \"vault\", \"mysqlServer\" or \"dataFactory\"." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "privateDnsZoneGroup": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + } + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "isManualConnection": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. If Manual Private Link Connection is required." + } + }, + "manualConnectionRequestMessage": { + "type": "string", + "nullable": true, + "maxLength": 140, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with the manual connection request." + } + }, + "customDnsConfigs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint ip address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private ip addresses of the private endpoint." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "ipConfigurations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private ip address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. Specify the type of lock." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "enableTelemetry": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "resourceGroupName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify if you want to deploy the Private Endpoint into a different resource group than the main resource." + } + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "secretsExportConfigurationType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the key vault where to store the secrets of this module." + } + }, + "connectionStringSecretName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name to use when creating the primary write connection string secret." + } + } + } + }, + "secretsOutputType": { + "type": "object", + "properties": {}, + "additionalProperties": { + "$ref": "#/definitions/secretSetType", + "metadata": { + "description": "An exported secret's references." + } + } + }, + "secretSetType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "modules/keyVaultExport.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Azure Cosmos DB MongoDB vCore cluster." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Default to current resource group scope location. Location for all resources." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags of the Database Account resource." + } + }, + "administratorLogin": { + "type": "string", + "metadata": { + "description": "Required. Username for admin user." + } + }, + "administratorLoginPassword": { + "type": "securestring", + "minLength": 8, + "maxLength": 128, + "metadata": { + "description": "Required. Password for admin user." + } + }, + "createMode": { + "type": "string", + "defaultValue": "Default", + "metadata": { + "description": "Optional. Mode to create the azure cosmos db mongodb vCore cluster." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "highAvailabilityMode": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Optional. Whether high availability is enabled on the node group." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "networkAcls": { + "$ref": "#/definitions/networkAclsType", + "nullable": true, + "metadata": { + "description": "Optional. IP addresses to allow access to the cluster from." + } + }, + "nodeCount": { + "type": "int", + "metadata": { + "description": "Required. Number of nodes in the node group." + } + }, + "nodeType": { + "type": "string", + "defaultValue": "Shard", + "metadata": { + "description": "Optional. Deployed Node type in the node group." + } + }, + "privateEndpoints": { + "$ref": "#/definitions/privateEndpointType", + "metadata": { + "description": "Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "secretsExportConfiguration": { + "$ref": "#/definitions/secretsExportConfigurationType", + "nullable": true, + "metadata": { + "description": "Optional. Key vault reference and secret settings for the module's secrets export." + } + }, + "sku": { + "type": "string", + "metadata": { + "description": "Required. SKU defines the CPU and memory that is provisioned for each node." + } + }, + "storage": { + "type": "int", + "metadata": { + "description": "Required. Disk storage size for the node group in GB." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + }, + "firewallRules": "[union(map(coalesce(tryGet(parameters('networkAcls'), 'customRules'), createArray()), lambda('customRule', createObject('name', coalesce(tryGet(lambdaVariables('customRule'), 'firewallRuleName'), format('allow-{0}-to-{1}', replace(lambdaVariables('customRule').startIpAddress, '.', ''), replace(lambdaVariables('customRule').endIpAddress, '.', ''))), 'startIpAddress', lambdaVariables('customRule').startIpAddress, 'endIpAddress', lambdaVariables('customRule').endIpAddress))), if(coalesce(tryGet(parameters('networkAcls'), 'allowAllIPs'), false()), createArray(createObject('name', 'allow-all-IPs', 'startIpAddress', '0.0.0.0', 'endIpAddress', '255.255.255.255')), createArray()), if(coalesce(tryGet(parameters('networkAcls'), 'allowAzureIPs'), false()), createArray(createObject('name', 'allow-all-azure-internal-IPs', 'startIpAddress', '0.0.0.0', 'endIpAddress', '0.0.0.0')), createArray()))]" + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.documentdb-mongocluster.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "mongoCluster": { + "type": "Microsoft.DocumentDB/mongoClusters", + "apiVersion": "2024-02-15-preview", + "name": "[parameters('name')]", + "tags": "[parameters('tags')]", + "location": "[parameters('location')]", + "properties": { + "administratorLogin": "[parameters('administratorLogin')]", + "administratorLoginPassword": "[parameters('administratorLoginPassword')]", + "createMode": "[parameters('createMode')]", + "nodeGroupSpecs": [ + { + "diskSizeGB": "[parameters('storage')]", + "enableHa": "[parameters('highAvailabilityMode')]", + "kind": "[parameters('nodeType')]", + "nodeCount": "[parameters('nodeCount')]", + "sku": "[parameters('sku')]" + } + ] + } + }, + "mongoCluster_diagnosticSettings": { + "copy": { + "name": "mongoCluster_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.DocumentDB/mongoClusters/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "mongoCluster" + ] + }, + "mongoCluster_roleAssignments": { + "copy": { + "name": "mongoCluster_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.DocumentDB/mongoClusters/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.DocumentDB/mongoClusters', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "mongoCluster" + ] + }, + "mongoCluster_configFireWallRules": { + "copy": { + "name": "mongoCluster_configFireWallRules", + "count": "[length(variables('firewallRules'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-firewallRule-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "mongoClusterName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[variables('firewallRules')[copyIndex()].name]" + }, + "startIpAddress": { + "value": "[variables('firewallRules')[copyIndex()].startIpAddress]" + }, + "endIpAddress": { + "value": "[variables('firewallRules')[copyIndex()].endIpAddress]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "11076682219298980277" + }, + "name": "Azure Cosmos DB MongoDB vCore Cluster Config FireWall Rules", + "description": "This module config firewall rules for the Azure Cosmos DB MongoDB vCore cluster.", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "mongoClusterName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Azure Cosmos DB MongoDB vCore cluster. Required if the template is used in a standalone deployment." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the firewall rule." + } + }, + "startIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The start IP address of the Azure Cosmos DB MongoDB vCore cluster firewall rule. Must be IPv4 format." + } + }, + "endIpAddress": { + "type": "string", + "metadata": { + "description": "Required. The end IP address of the Azure Cosmos DB MongoDB vCore cluster firewall rule. Must be IPv4 format." + } + } + }, + "resources": [ + { + "type": "Microsoft.DocumentDB/mongoClusters/firewallRules", + "apiVersion": "2024-02-15-preview", + "name": "[format('{0}/{1}', parameters('mongoClusterName'), parameters('name'))]", + "properties": { + "startIpAddress": "[parameters('startIpAddress')]", + "endIpAddress": "[parameters('endIpAddress')]" + } + } + ], + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the Azure Cosmos DB MongoDB vCore cluster was created in." + }, + "value": "[resourceGroup().name]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the firewall rule." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the firewall rule." + }, + "value": "[resourceId('Microsoft.DocumentDB/mongoClusters/firewallRules', parameters('mongoClusterName'), parameters('name'))]" + } + } + } + }, + "dependsOn": [ + "mongoCluster" + ] + }, + "secretsExport": { + "condition": "[not(equals(parameters('secretsExportConfiguration'), null()))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-secrets-kv', uniqueString(deployment().name, parameters('location')))]", + "subscriptionId": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/')[2]]", + "resourceGroup": "[split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '////'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "keyVaultName": { + "value": "[last(split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/'))]" + }, + "secretsToSet": { + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'connectionStringSecretName'), createArray(createObject('name', parameters('secretsExportConfiguration').connectionStringSecretName, 'value', reference('mongoCluster').connectionString)), createArray()))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.27.1.19265", + "templateHash": "4835482779180953345" + } + }, + "definitions": { + "secretSetType": { + "type": "object", + "properties": { + "secretResourceId": { + "type": "string", + "metadata": { + "description": "The resourceId of the exported secret." + } + }, + "secretUri": { + "type": "string", + "metadata": { + "description": "The secret URI of the exported secret." + } + } + }, + "metadata": { + "__bicep_export!": true + } + }, + "secretToSetType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the secret to set." + } + }, + "value": { + "type": "securestring", + "metadata": { + "description": "Required. The value of the secret to set." + } + } + } + } + }, + "parameters": { + "keyVaultName": { + "type": "string", + "metadata": { + "description": "Required. The name of the Key Vault to set the secrets in." + } + }, + "secretsToSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretToSetType" + }, + "metadata": { + "description": "Required. The secrets to set in the Key Vault." + } + } + }, + "resources": { + "keyVault": { + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[parameters('keyVaultName')]" + }, + "secrets": { + "copy": { + "name": "secrets", + "count": "[length(parameters('secretsToSet'))]" + }, + "type": "Microsoft.KeyVault/vaults/secrets", + "apiVersion": "2023-07-01", + "name": "[format('{0}/{1}', parameters('keyVaultName'), parameters('secretsToSet')[copyIndex()].name)]", + "properties": { + "value": "[parameters('secretsToSet')[copyIndex()].value]" + }, + "dependsOn": [ + "keyVault" + ] + } + }, + "outputs": { + "secretsSet": { + "type": "array", + "items": { + "$ref": "#/definitions/secretSetType" + }, + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "copy": { + "count": "[length(range(0, length(coalesce(parameters('secretsToSet'), createArray()))))]", + "input": { + "secretResourceId": "[resourceId('Microsoft.KeyVault/vaults/secrets', parameters('keyVaultName'), parameters('secretsToSet')[range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()]].name)]", + "secretUri": "[reference(format('secrets[{0}]', range(0, length(coalesce(parameters('secretsToSet'), createArray())))[copyIndex()])).secretUri]" + } + } + } + } + } + }, + "dependsOn": [ + "mongoCluster" + ] + }, + "mongoCluster_privateEndpoints": { + "copy": { + "name": "mongoCluster_privateEndpoints", + "count": "[length(coalesce(parameters('privateEndpoints'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-databaseAccount-PrivateEndpoint-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "resourceGroup": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'resourceGroupName'), '')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'name'), format('pep-{0}-{1}-{2}', last(split(resourceId('Microsoft.DocumentDB/mongoClusters', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'mongoCluster'), copyIndex()))]" + }, + "privateLinkServiceConnections": "[if(not(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true())), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.DocumentDB/mongoClusters', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'mongoCluster'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.DocumentDB/mongoClusters', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'mongoCluster')))))), createObject('value', null()))]", + "manualPrivateLinkServiceConnections": "[if(equals(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'isManualConnection'), true()), createObject('value', createArray(createObject('name', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateLinkServiceConnectionName'), format('{0}-{1}-{2}', last(split(resourceId('Microsoft.DocumentDB/mongoClusters', parameters('name')), '/')), coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'mongoCluster'), copyIndex())), 'properties', createObject('privateLinkServiceId', resourceId('Microsoft.DocumentDB/mongoClusters', parameters('name')), 'groupIds', createArray(coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'service'), 'mongoCluster')), 'requestMessage', coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'manualConnectionRequestMessage'), 'Manual approval required.'))))), createObject('value', null()))]", + "subnetResourceId": { + "value": "[coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId]" + }, + "enableTelemetry": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'enableTelemetry'), parameters('enableTelemetry'))]" + }, + "location": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'location'), reference(split(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()].subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location)]" + }, + "lock": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'lock'), parameters('lock'))]" + }, + "privateDnsZoneGroup": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'privateDnsZoneGroup')]" + }, + "roleAssignments": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "tags": { + "value": "[coalesce(tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'tags'), parameters('tags'))]" + }, + "customDnsConfigs": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customDnsConfigs')]" + }, + "ipConfigurations": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'ipConfigurations')]" + }, + "applicationSecurityGroupResourceIds": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'applicationSecurityGroupResourceIds')]" + }, + "customNetworkInterfaceName": { + "value": "[tryGet(coalesce(parameters('privateEndpoints'), createArray())[copyIndex()], 'customNetworkInterfaceName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "1277254088602407590" + }, + "name": "Private Endpoints", + "description": "This module deploys a Private Endpoint.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "privateDnsZoneGroupType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the Private DNS Zone Group." + } + }, + "privateDnsZoneGroupConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "metadata": { + "description": "Required. The private DNS zone groups to associate the private endpoint. A DNS zone group can support up to 5 DNS zones." + } + } + } + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "ipConfigurationsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the resource that is unique within a resource group." + } + }, + "properties": { + "type": "object", + "properties": { + "groupId": { + "type": "string", + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "memberName": { + "type": "string", + "metadata": { + "description": "Required. The member name of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string." + } + }, + "privateIPAddress": { + "type": "string", + "metadata": { + "description": "Required. A private IP address obtained from the private endpoint's subnet." + } + } + }, + "metadata": { + "description": "Required. Properties of private endpoint IP configurations." + } + } + } + }, + "nullable": true + }, + "manualPrivateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "privateLinkServiceConnectionsType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the private link service connection." + } + }, + "properties": { + "type": "object", + "properties": { + "groupIds": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. The ID of a group obtained from the remote resource that this private endpoint should connect to. If used with private link service connection, this property must be defined as empty string array `[]`." + } + }, + "privateLinkServiceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of private link service." + } + }, + "requestMessage": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. A message passed to the owner of the remote resource with this connection request. Restricted to 140 chars." + } + } + }, + "metadata": { + "description": "Required. Properties of private link service connection." + } + } + } + }, + "nullable": true + }, + "customDnsConfigType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "fqdn": { + "type": "string", + "metadata": { + "description": "Required. Fqdn that resolves to private endpoint IP address." + } + }, + "ipAddresses": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Required. A list of private IP addresses of the private endpoint." + } + } + } + }, + "nullable": true + }, + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_imported_from!": { + "sourceTemplate": "private-dns-zone-group/main.bicep" + } + } + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the private endpoint resource to create." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. Resource ID of the subnet where the endpoint needs to be created." + } + }, + "applicationSecurityGroupResourceIds": { + "type": "array", + "nullable": true, + "metadata": { + "description": "Optional. Application security groups in which the private endpoint IP configuration is included." + } + }, + "customNetworkInterfaceName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The custom name of the network interface attached to the private endpoint." + } + }, + "ipConfigurations": { + "$ref": "#/definitions/ipConfigurationsType", + "metadata": { + "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." + } + }, + "privateDnsZoneGroup": { + "$ref": "#/definitions/privateDnsZoneGroupType", + "nullable": true, + "metadata": { + "description": "Optional. The private DNS zone group to configure for the private endpoint." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." + } + }, + "customDnsConfigs": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "Optional. Custom DNS configurations." + } + }, + "manualPrivateLinkServiceConnections": { + "$ref": "#/definitions/manualPrivateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource." + } + }, + "privateLinkServiceConnections": { + "$ref": "#/definitions/privateLinkServiceConnectionsType", + "metadata": { + "description": "Optional. A grouping of information about the connection to the remote resource." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "DNS Resolver Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0f2ebee7-ffd4-4fc0-b3b7-664099fdad5d')]", + "DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'befefa01-2a29-4197-83a8-272ff33ce314')]", + "Domain Services Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'eeaeda52-9324-47f6-8069-5d5bade478b2')]", + "Domain Services Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '361898ef-9ed1-48c2-849c-a832951106bb')]", + "Network Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '4d97b98b-1d4f-4787-a291-c67834d212e7')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Private DNS Zone Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b12aa53e-6015-4669-85d0-8515ebb3ae7f')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.7.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "privateEndpoint": { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "copy": [ + { + "name": "applicationSecurityGroups", + "count": "[length(coalesce(parameters('applicationSecurityGroupResourceIds'), createArray()))]", + "input": { + "id": "[coalesce(parameters('applicationSecurityGroupResourceIds'), createArray())[copyIndex('applicationSecurityGroups')]]" + } + } + ], + "customDnsConfigs": "[coalesce(parameters('customDnsConfigs'), createArray())]", + "customNetworkInterfaceName": "[coalesce(parameters('customNetworkInterfaceName'), '')]", + "ipConfigurations": "[coalesce(parameters('ipConfigurations'), createArray())]", + "manualPrivateLinkServiceConnections": "[coalesce(parameters('manualPrivateLinkServiceConnections'), createArray())]", + "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + }, + "privateEndpoint_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_roleAssignments": { + "copy": { + "name": "privateEndpoint_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + }, + "privateEndpoint_privateDnsZoneGroup": { + "condition": "[not(empty(parameters('privateDnsZoneGroup')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-PrivateEndpoint-PrivateDnsZoneGroup', uniqueString(deployment().name))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "name": { + "value": "[tryGet(parameters('privateDnsZoneGroup'), 'name')]" + }, + "privateEndpointName": { + "value": "[parameters('name')]" + }, + "privateDnsZoneConfigs": { + "value": "[parameters('privateDnsZoneGroup').privateDnsZoneGroupConfigs]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.29.47.4906", + "templateHash": "5805178546717255803" + }, + "name": "Private Endpoint Private DNS Zone Groups", + "description": "This module deploys a Private Endpoint Private DNS Zone Group.", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "privateDnsZoneGroupConfigType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the private DNS zone group config." + } + }, + "privateDnsZoneResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource id of the private DNS zone." + } + } + }, + "metadata": { + "__bicep_export!": true + } + } + }, + "parameters": { + "privateEndpointName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent private endpoint. Required if the template is used in a standalone deployment." + } + }, + "privateDnsZoneConfigs": { + "type": "array", + "items": { + "$ref": "#/definitions/privateDnsZoneGroupConfigType" + }, + "minLength": 1, + "maxLength": 5, + "metadata": { + "description": "Required. Array of private DNS zone configurations of the private DNS zone group. A DNS zone group can support up to 5 DNS zones." + } + }, + "name": { + "type": "string", + "defaultValue": "default", + "metadata": { + "description": "Optional. The name of the private DNS zone group." + } + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneConfigsVar", + "count": "[length(parameters('privateDnsZoneConfigs'))]", + "input": { + "name": "[coalesce(tryGet(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')], 'name'), last(split(parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId, '/')))]", + "properties": { + "privateDnsZoneId": "[parameters('privateDnsZoneConfigs')[copyIndex('privateDnsZoneConfigsVar')].privateDnsZoneResourceId]" + } + } + } + ] + }, + "resources": { + "privateEndpoint": { + "existing": true, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-11-01", + "name": "[parameters('privateEndpointName')]" + }, + "privateDnsZoneGroup": { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", + "properties": { + "privateDnsZoneConfigs": "[variables('privateDnsZoneConfigsVar')]" + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint DNS zone group." + }, + "value": "[parameters('name')]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint DNS zone group." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', parameters('privateEndpointName'), parameters('name'))]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint DNS zone group was deployed into." + }, + "value": "[resourceGroup().name]" + } + } + } + }, + "dependsOn": [ + "privateEndpoint" + ] + } + }, + "outputs": { + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The resource group the private endpoint was deployed into." + }, + "value": "[resourceGroup().name]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the private endpoint." + }, + "value": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the private endpoint." + }, + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('privateEndpoint', '2023-11-01', 'full').location]" + }, + "customDnsConfig": { + "$ref": "#/definitions/customDnsConfigType", + "metadata": { + "description": "The custom DNS configurations of the private endpoint." + }, + "value": "[reference('privateEndpoint').customDnsConfigs]" + }, + "networkInterfaceIds": { + "type": "array", + "metadata": { + "description": "The IDs of the network interfaces associated with the private endpoint." + }, + "value": "[reference('privateEndpoint').networkInterfaces]" + }, + "groupId": { + "type": "string", + "metadata": { + "description": "The group Id for the private endpoint Group." + }, + "value": "[if(and(not(empty(reference('privateEndpoint').manualPrivateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').manualPrivateLinkServiceConnections[0].properties, 'groupIds', 0), ''), if(and(not(empty(reference('privateEndpoint').privateLinkServiceConnections)), greater(length(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds')), 0)), coalesce(tryGet(reference('privateEndpoint').privateLinkServiceConnections[0].properties, 'groupIds', 0), ''), ''))]" + } + } + } + }, + "dependsOn": [ + "mongoCluster" + ] + } + }, + "outputs": { + "name": { + "type": "string", + "metadata": { + "description": "The name of the Azure Cosmos DB MongoDB vCore cluster." + }, + "value": "[parameters('name')]" + }, + "mongoClusterResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Azure Cosmos DB MongoDB vCore cluster." + }, + "value": "[resourceId('Microsoft.DocumentDB/mongoClusters', parameters('name'))]" + }, + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the resource group the firewall rule was created in." + }, + "value": "[resourceGroup().id]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the firewall rule was created in." + }, + "value": "[resourceGroup().name]" + }, + "connectionStringKey": { + "type": "string", + "metadata": { + "description": "The connection string key of the mongo cluster." + }, + "value": "[reference('mongoCluster').connectionString]" + }, + "firewallRules": { + "type": "array", + "items": { + "$ref": "#/definitions/firewallSetType" + }, + "metadata": { + "description": "The name and resource ID of firewall rule." + }, + "copy": { + "count": "[length(range(0, length(coalesce(variables('firewallRules'), createArray()))))]", + "input": { + "name": "[reference(format('mongoCluster_configFireWallRules[{0}]', range(0, length(coalesce(variables('firewallRules'), createArray())))[copyIndex()])).outputs.name.value]", + "resourceId": "[reference(format('mongoCluster_configFireWallRules[{0}]', range(0, length(coalesce(variables('firewallRules'), createArray())))[copyIndex()])).outputs.resourceId.value]" + } + } + }, + "privateEndpoints": { + "type": "array", + "metadata": { + "description": "The private endpoints of the database account." + }, + "copy": { + "count": "[length(if(not(empty(parameters('privateEndpoints'))), array(parameters('privateEndpoints')), createArray()))]", + "input": { + "name": "[reference(format('mongoCluster_privateEndpoints[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('mongoCluster_privateEndpoints[{0}]', copyIndex())).outputs.resourceId.value]", + "groupId": "[reference(format('mongoCluster_privateEndpoints[{0}]', copyIndex())).outputs.groupId.value]", + "customDnsConfig": "[reference(format('mongoCluster_privateEndpoints[{0}]', copyIndex())).outputs.customDnsConfig.value]", + "networkInterfaceIds": "[reference(format('mongoCluster_privateEndpoints[{0}]', copyIndex())).outputs.networkInterfaceIds.value]" + } + } + }, + "exportedSecrets": { + "$ref": "#/definitions/secretsOutputType", + "metadata": { + "description": "The references to the secrets exported to the provided Key Vault." + }, + "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" + } + } +} \ No newline at end of file diff --git a/avm/res/document-db/mongo-cluster/modules/keyVaultExport.bicep b/avm/res/document-db/mongo-cluster/modules/keyVaultExport.bicep new file mode 100644 index 0000000000..1bd329ba99 --- /dev/null +++ b/avm/res/document-db/mongo-cluster/modules/keyVaultExport.bicep @@ -0,0 +1,50 @@ +@description('Required. The name of the Key Vault to set the secrets in.') +param keyVaultName string + +@description('Required. The secrets to set in the Key Vault.') +param secretsToSet secretToSetType[] + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +resource secrets 'Microsoft.KeyVault/vaults/secrets@2023-07-01' = [ + for secret in secretsToSet: { + name: secret.name + parent: keyVault + properties: { + value: secret.value + } + } +] + +@description('The references to the secrets exported to the provided Key Vault.') +output secretsSet secretSetType[] = [ + #disable-next-line outputs-should-not-contain-secrets // Only returning the references, not a secret value + for index in range(0, length(secretsToSet ?? [])): { + secretResourceId: secrets[index].id + secretUri: secrets[index].properties.secretUri + } +] + +// =============== // +// Definitions // +// =============== // + +@export() +type secretSetType = { + @description('The resourceId of the exported secret.') + secretResourceId: string + + @description('The secret URI of the exported secret.') + secretUri: string +} + +type secretToSetType = { + @description('Required. The name of the secret to set.') + name: string + + @description('Required. The value of the secret to set.') + @secure() + value: string +} diff --git a/avm/res/document-db/mongo-cluster/tests/e2e/defaults/main.test.bicep b/avm/res/document-db/mongo-cluster/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..536ed8e984 --- /dev/null +++ b/avm/res/document-db/mongo-cluster/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,57 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-documentdb-mongoclusters-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'ddmcdefmin' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. The password to leverage for the login.') +@secure() +param password string = newGuid() + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + administratorLogin: 'Admin001' + administratorLoginPassword: password + nodeCount: 2 + sku: 'M30' + storage: 256 + } + } +] diff --git a/avm/res/document-db/mongo-cluster/tests/e2e/kvSecrets/dependencies.bicep b/avm/res/document-db/mongo-cluster/tests/e2e/kvSecrets/dependencies.bicep new file mode 100644 index 0000000000..d3eadbfb8f --- /dev/null +++ b/avm/res/document-db/mongo-cluster/tests/e2e/kvSecrets/dependencies.bicep @@ -0,0 +1,21 @@ +@description('Optional. The location to deploy to.') +param location string = resourceGroup().location + +@description('Required. The name of the Managed Identity to create.') +param keyVaultName string + +resource keyVault 'Microsoft.KeyVault/vaults@2021-06-01-preview' = { + name: keyVaultName + location: location + properties: { + sku: { + family: 'A' + name: 'standard' + } + enableRbacAuthorization: true + tenantId: subscription().tenantId + } +} + +@description('The resource Id of the Key Vault created.') +output keyVaultResourceId string = keyVault.id diff --git a/avm/res/document-db/mongo-cluster/tests/e2e/kvSecrets/main.test.bicep b/avm/res/document-db/mongo-cluster/tests/e2e/kvSecrets/main.test.bicep new file mode 100644 index 0000000000..82a5ecbb26 --- /dev/null +++ b/avm/res/document-db/mongo-cluster/tests/e2e/kvSecrets/main.test.bicep @@ -0,0 +1,72 @@ +targetScope = 'subscription' + +metadata name = 'Deploying with a key vault reference to save secrets' +metadata description = 'This instance deploys the module saving its secrets in a key vault.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-documentdb.databaseaccounts-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'dddaskvs' + +@description('Optional. A token to inject into the name of each resource.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. The password to leverage for the login.') +@secure() +param password string = newGuid() + +// ============== // +// General resources +// ============== // +resource resourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + keyVaultName: 'dep-${namePrefix}-kv-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +module testDeployment '../../../main.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}' + params: { + location: resourceLocation + name: '${namePrefix}-kv-ref' + secretsExportConfiguration: { + keyVaultResourceId: nestedDependencies.outputs.keyVaultResourceId + connectionStringSecretName: 'connectionString' + } + administratorLogin: 'Admin002' + administratorLoginPassword: password + nodeCount: 2 + sku: 'M30' + storage: 256 + } +} + +// Output usage examples +output specificSecret string = testDeployment.outputs.exportedSecrets.connectionString.secretResourceId +output allEportedSecrets object = testDeployment.outputs.exportedSecrets +output allExportedSecretResourceIds array = map( + items(testDeployment.outputs.exportedSecrets), + item => item.value.secretResourceId +) diff --git a/avm/res/document-db/mongo-cluster/tests/e2e/max/dependencies.bicep b/avm/res/document-db/mongo-cluster/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..16d9a64d1b --- /dev/null +++ b/avm/res/document-db/mongo-cluster/tests/e2e/max/dependencies.bicep @@ -0,0 +1,102 @@ +@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 Deployment Script to create to get the paired region name.') +param pairedRegionScriptName string + +var addressPrefix = '10.0.0.0/16' + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-04-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 16, 0) + } + } + ] + } +} + +resource privateDNSZone 'Microsoft.Network/privateDnsZones@2020-06-01' = { + name: 'privatelink${environment().suffixes.acrLoginServer}' + location: 'global' + + resource virtualNetworkLinks 'virtualNetworkLinks@2020-06-01' = { + name: '${virtualNetwork.name}-vnetlink' + location: 'global' + properties: { + virtualNetwork: { + id: virtualNetwork.id + } + registrationEnabled: false + } + } +} + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = { + name: managedIdentityName + location: location +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('msi-${location}-${managedIdentity.id}-Reader-RoleAssignment') + properties: { + principalId: managedIdentity.properties.principalId + roleDefinitionId: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) // Reader + principalType: 'ServicePrincipal' + } +} + +resource getPairedRegionScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: pairedRegionScriptName + location: location + kind: 'AzurePowerShell' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentity.id}': {} + } + } + properties: { + azPowerShellVersion: '8.0' + retentionInterval: 'P1D' + arguments: '-Location \\"${location}\\"' + scriptContent: loadTextContent('../../../../../../utilities/e2e-template-assets/scripts/Get-PairedRegion.ps1') + } + dependsOn: [ + roleAssignment + ] +} + +@description('The resource ID of the created Virtual Network Subnet.') +output subnetResourceId string = virtualNetwork.properties.subnets[0].id + +@description('The principal ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created Managed Identity.') +output managedIdentityResourceId string = managedIdentity.id + +@description('The resource ID of the created Private DNS Zone.') +output privateDNSZoneResourceId string = privateDNSZone.id + +@description('The name of the paired region.') +output pairedRegionName string = getPairedRegionScript.properties.outputs.pairedRegionName diff --git a/avm/res/document-db/mongo-cluster/tests/e2e/max/main.test.bicep b/avm/res/document-db/mongo-cluster/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..e8127df0e3 --- /dev/null +++ b/avm/res/document-db/mongo-cluster/tests/e2e/max/main.test.bicep @@ -0,0 +1,163 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set' +metadata description = 'This instance deploys the module with the maximum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-documentdb-mongoclusters-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'ddmcmax' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. The password to leverage for the login.') +@secure() +param password string = newGuid() + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + managedIdentityName: 'dep-${namePrefix}-msi-ds-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + pairedRegionScriptName: 'dep-${namePrefix}-ds-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + administratorLogin: 'Admin003' + administratorLoginPassword: password + createMode: 'Default' + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + highAvailabilityMode: false + networkAcls: { + customRules: [ + { + firewallRuleName: 'allow-1.2.3.4-to-5.6.7.8' + endIpAddress: '5.6.7.8' + startIpAddress: '1.2.3.4' + } + ] + allowAzureIPs: true + allowAllIPs: true + } + nodeCount: 2 + nodeType: 'Shard' + privateEndpoints: [ + { + subnetResourceId: nestedDependencies.outputs.subnetResourceId + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + } + { + subnetResourceId: nestedDependencies.outputs.subnetResourceId + privateDnsZoneGroup: { + privateDnsZoneGroupConfigs: [ + { + privateDnsZoneResourceId: nestedDependencies.outputs.privateDNSZoneResourceId + } + ] + } + } + ] + roleAssignments: [ + { + name: '60395919-cfd3-47bf-8349-775ddebb255e' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + name: guid('Custom seed ${namePrefix}${serviceShort}') + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + sku: 'M30' + storage: 256 + } + dependsOn: [ + nestedDependencies + diagnosticDependencies + ] + } +] diff --git a/avm/res/document-db/mongo-cluster/tests/e2e/waf-aligned/main.test.bicep b/avm/res/document-db/mongo-cluster/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..4d098959c8 --- /dev/null +++ b/avm/res/document-db/mongo-cluster/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,57 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-documentdb-mongoclusters-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'ddmcwaf' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +@description('Optional. The password to leverage for the login.') +@secure() +param password string = newGuid() + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + administratorLogin: 'Admin001' + administratorLoginPassword: password + nodeCount: 2 + sku: 'M30' + storage: 256 + } + } +] diff --git a/avm/res/document-db/mongo-cluster/version.json b/avm/res/document-db/mongo-cluster/version.json new file mode 100644 index 0000000000..eb30921b0c --- /dev/null +++ b/avm/res/document-db/mongo-cluster/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} \ No newline at end of file From 0589cbe166fd76f6c79db40a93b1e44cc28fda0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20H=C3=A9zser?= Date: Wed, 2 Oct 2024 20:33:28 +0200 Subject: [PATCH 55/57] feat: update TLS versions to supported values `avm/res/storage/storage-account` (#3391) ## Description - bumps the API Version to 2023-05-01 - added TLS 1.3 - removed TLS 1.0 and 1.1 Closes #3313 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.storage.storage-account](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml/badge.svg?branch=storage-account-tls-version)](https://github.com/ReneHezser/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [ ] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [x] The bug was found by the module author, and no one has opened an issue to report it yet. - [x] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/storage/storage-account/README.md | 9 ++++----- avm/res/storage/storage-account/main.bicep | 7 +++---- avm/res/storage/storage-account/main.json | 19 +++++++++---------- avm/res/storage/storage-account/version.json | 2 +- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/avm/res/storage/storage-account/README.md b/avm/res/storage/storage-account/README.md index 310335b0d2..8422599676 100644 --- a/avm/res/storage/storage-account/README.md +++ b/avm/res/storage/storage-account/README.md @@ -22,7 +22,7 @@ This module deploys a Storage Account. | `Microsoft.KeyVault/vaults/secrets` | [2023-07-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.KeyVault/2023-07-01/vaults/secrets) | | `Microsoft.Network/privateEndpoints` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints) | | `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Network/2023-11-01/privateEndpoints/privateDnsZoneGroups) | -| `Microsoft.Storage/storageAccounts` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts) | +| `Microsoft.Storage/storageAccounts` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2023-05-01/storageAccounts) | | `Microsoft.Storage/storageAccounts/blobServices` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices) | | `Microsoft.Storage/storageAccounts/blobServices/containers` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers) | | `Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies` | [2022-09-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Storage/2022-09-01/storageAccounts/blobServices/containers/immutabilityPolicies) | @@ -2255,7 +2255,7 @@ module storageAccount 'br/public:avm/res/storage/storage-account:' = { | [`lock`](#parameter-lock) | object | The lock settings of the service. | | [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. | | [`managementPolicyRules`](#parameter-managementpolicyrules) | array | The Storage Account ManagementPolicies Rules. | -| [`minimumTlsVersion`](#parameter-minimumtlsversion) | string | Set the minimum TLS version on request to storage. | +| [`minimumTlsVersion`](#parameter-minimumtlsversion) | string | Set the minimum TLS version on request to storage. The TLS versions 1.0 and 1.1 are deprecated and not supported anymore. | | [`networkAcls`](#parameter-networkacls) | object | Networks ACLs, this value contains IPs to whitelist and/or Subnet information. If in use, bypass needs to be supplied. For security reasons, it is recommended to set the DefaultAction Deny. | | [`privateEndpoints`](#parameter-privateendpoints) | array | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | | [`publicNetworkAccess`](#parameter-publicnetworkaccess) | string | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set and networkAcls are not set. | @@ -2730,7 +2730,7 @@ The Storage Account ManagementPolicies Rules. ### Parameter: `minimumTlsVersion` -Set the minimum TLS version on request to storage. +Set the minimum TLS version on request to storage. The TLS versions 1.0 and 1.1 are deprecated and not supported anymore. - Required: No - Type: string @@ -2738,9 +2738,8 @@ Set the minimum TLS version on request to storage. - Allowed: ```Bicep [ - 'TLS1_0' - 'TLS1_1' 'TLS1_2' + 'TLS1_3' ] ``` diff --git a/avm/res/storage/storage-account/main.bicep b/avm/res/storage/storage-account/main.bicep index 014c244abc..b8b321d82c 100644 --- a/avm/res/storage/storage-account/main.bicep +++ b/avm/res/storage/storage-account/main.bicep @@ -114,11 +114,10 @@ param tableServices object = {} param allowBlobPublicAccess bool = false @allowed([ - 'TLS1_0' - 'TLS1_1' 'TLS1_2' + 'TLS1_3' ]) -@description('Optional. Set the minimum TLS version on request to storage.') +@description('Optional. Set the minimum TLS version on request to storage. The TLS versions 1.0 and 1.1 are deprecated and not supported anymore.') param minimumTlsVersion string = 'TLS1_2' @description('Conditional. If true, enables Hierarchical Namespace for the storage account. Required if enableSftp or enableNfsV3 is set to true.') @@ -341,7 +340,7 @@ resource cMKUserAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentiti ) } -resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = { +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = { name: name location: location kind: kind diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index 90cce4364c..3f89f10c57 100644 --- a/avm/res/storage/storage-account/main.json +++ b/avm/res/storage/storage-account/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "6607893452071234065" + "templateHash": "7109049210516898074" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account.", @@ -816,12 +816,11 @@ "type": "string", "defaultValue": "TLS1_2", "allowedValues": [ - "TLS1_0", - "TLS1_1", - "TLS1_2" + "TLS1_2", + "TLS1_3" ], "metadata": { - "description": "Optional. Set the minimum TLS version on request to storage." + "description": "Optional. Set the minimum TLS version on request to storage. The TLS versions 1.0 and 1.1 are deprecated and not supported anymore." } }, "enableHierarchicalNamespace": { @@ -1040,7 +1039,7 @@ }, "storageAccount": { "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2022-09-01", + "apiVersion": "2023-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "kind": "[parameters('kind')]", @@ -4906,7 +4905,7 @@ "value": "[last(split(coalesce(tryGet(parameters('secretsExportConfiguration'), 'keyVaultResourceId'), '//'), '/'))]" }, "secretsToSet": { - "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1'), createArray(createObject('name', parameters('secretsExportConfiguration').accessKey1, 'value', listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2022-09-01').keys[0].value)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'connectionString1'), createArray(createObject('name', parameters('secretsExportConfiguration').connectionString1, 'value', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix=core.windows.net', parameters('name'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2022-09-01').keys[0].value))), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2'), createArray(createObject('name', parameters('secretsExportConfiguration').accessKey2, 'value', listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2022-09-01').keys[1].value)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'connectionString2'), createArray(createObject('name', parameters('secretsExportConfiguration').connectionString2, 'value', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix=core.windows.net', parameters('name'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2022-09-01').keys[1].value))), createArray()))]" + "value": "[union(createArray(), if(contains(parameters('secretsExportConfiguration'), 'accessKey1'), createArray(createObject('name', parameters('secretsExportConfiguration').accessKey1, 'value', listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2023-05-01').keys[0].value)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'connectionString1'), createArray(createObject('name', parameters('secretsExportConfiguration').connectionString1, 'value', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix=core.windows.net', parameters('name'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2023-05-01').keys[0].value))), createArray()), if(contains(parameters('secretsExportConfiguration'), 'accessKey2'), createArray(createObject('name', parameters('secretsExportConfiguration').accessKey2, 'value', listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2023-05-01').keys[1].value)), createArray()), if(contains(parameters('secretsExportConfiguration'), 'connectionString2'), createArray(createObject('name', parameters('secretsExportConfiguration').connectionString2, 'value', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix=core.windows.net', parameters('name'), listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('name')), '2023-05-01').keys[1].value))), createArray()))]" } }, "template": { @@ -5058,14 +5057,14 @@ "metadata": { "description": "The principal ID of the system assigned identity." }, - "value": "[coalesce(tryGet(tryGet(reference('storageAccount', '2022-09-01', 'full'), 'identity'), 'principalId'), '')]" + "value": "[coalesce(tryGet(tryGet(reference('storageAccount', '2023-05-01', 'full'), 'identity'), 'principalId'), '')]" }, "location": { "type": "string", "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('storageAccount', '2022-09-01', 'full').location]" + "value": "[reference('storageAccount', '2023-05-01', 'full').location]" }, "serviceEndpoints": { "type": "object", @@ -5098,4 +5097,4 @@ "value": "[if(not(equals(parameters('secretsExportConfiguration'), null())), toObject(reference('secretsExport').outputs.secretsSet.value, lambda('secret', last(split(lambdaVariables('secret').secretResourceId, '/'))), lambda('secret', lambdaVariables('secret'))), createObject())]" } } -} \ No newline at end of file +} diff --git a/avm/res/storage/storage-account/version.json b/avm/res/storage/storage-account/version.json index 291fb73e82..35d09f6dbf 100644 --- a/avm/res/storage/storage-account/version.json +++ b/avm/res/storage/storage-account/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.13", + "version": "0.14", "pathFilters": [ "./main.json" ] From e549f47b7e6f127c8a5b2eeece5ab364173fb757 Mon Sep 17 00:00:00 2001 From: Kris Baranek <20225789+krbar@users.noreply.github.com> Date: Wed, 2 Oct 2024 21:05:59 +0200 Subject: [PATCH 56/57] feat: New module `avm/res/service-networking/traffic-controller` (#3169) ## Description New Module `avm/res/service-networking/traffic-controller` (Application Gateway for Containers). Related Module Proposal: https://github.com/Azure/Azure-Verified-Modules/issues/712 ## Adding a new module - [x] A proposal has been submitted and approved. - [ ] I have included "Closes #{module_proposal_issue_number}" in the PR description. - [ ] I have run `brm validate` locally to verify the module files. - [x] I have run deployment tests locally to ensure the module is deployable. ## Pipeline references | Pipeline | | - | | [![avm.res.service-networking.traffic-controller](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.service-networking.traffic-controller.yml/badge.svg?branch=users%2Fkrbar%2FtrafficControllerModule)](https://github.com/krbar/bicep-registry-modules/actions/workflows/avm.res.service-networking.traffic-controller.yml) | --------- Co-authored-by: Alexander Sehr --- .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 + ....service-networking.traffic-controller.yml | 88 ++ .../traffic-controller/README.md | 769 ++++++++++++++++++ .../traffic-controller/association/README.md | 79 ++ .../traffic-controller/association/main.bicep | 51 ++ .../traffic-controller/association/main.json | 85 ++ .../traffic-controller/frontend/README.md | 66 ++ .../traffic-controller/frontend/main.bicep | 43 + .../traffic-controller/frontend/main.json | 74 ++ .../traffic-controller/main.bicep | 301 +++++++ .../traffic-controller/main.json | 742 +++++++++++++++++ .../tests/e2e/defaults/main.test.bicep | 48 ++ .../tests/e2e/max/dependencies.bicep | 49 ++ .../tests/e2e/max/main.test.bicep | 131 +++ .../tests/e2e/waf-aligned/dependencies.bicep | 38 + .../tests/e2e/waf-aligned/main.test.bicep | 98 +++ .../traffic-controller/version.json | 7 + 18 files changed, 2671 insertions(+) create mode 100644 .github/workflows/avm.res.service-networking.traffic-controller.yml create mode 100644 avm/res/service-networking/traffic-controller/README.md create mode 100644 avm/res/service-networking/traffic-controller/association/README.md create mode 100644 avm/res/service-networking/traffic-controller/association/main.bicep create mode 100644 avm/res/service-networking/traffic-controller/association/main.json create mode 100644 avm/res/service-networking/traffic-controller/frontend/README.md create mode 100644 avm/res/service-networking/traffic-controller/frontend/main.bicep create mode 100644 avm/res/service-networking/traffic-controller/frontend/main.json create mode 100644 avm/res/service-networking/traffic-controller/main.bicep create mode 100644 avm/res/service-networking/traffic-controller/main.json create mode 100644 avm/res/service-networking/traffic-controller/tests/e2e/defaults/main.test.bicep create mode 100644 avm/res/service-networking/traffic-controller/tests/e2e/max/dependencies.bicep create mode 100644 avm/res/service-networking/traffic-controller/tests/e2e/max/main.test.bicep create mode 100644 avm/res/service-networking/traffic-controller/tests/e2e/waf-aligned/dependencies.bicep create mode 100644 avm/res/service-networking/traffic-controller/tests/e2e/waf-aligned/main.test.bicep create mode 100644 avm/res/service-networking/traffic-controller/version.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2e2123c29c..400d91ac7a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -147,6 +147,7 @@ /avm/res/search/search-service/ @Azure/avm-res-search-searchservice-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/service-bus/namespace/ @Azure/avm-res-servicebus-namespace-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/service-fabric/cluster/ @Azure/avm-res-servicefabric-cluster-module-owners-bicep @Azure/avm-module-reviewers-bicep +/avm/res/service-networking/traffic-controller/ @Azure/avm-res-servicenetworking-trafficcontroller-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/signal-r-service/signal-r/ @Azure/avm-res-signalrservice-signalr-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/signal-r-service/web-pub-sub/ @Azure/avm-res-signalrservice-webpubsub-module-owners-bicep @Azure/avm-module-reviewers-bicep /avm/res/sql/instance-pool/ @Azure/avm-res-sql-instancepool-module-owners-bicep @Azure/avm-module-reviewers-bicep diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml index d9e7855858..f5f78969d2 100644 --- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml +++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml @@ -182,6 +182,7 @@ body: - "avm/res/search/search-service" - "avm/res/service-bus/namespace" - "avm/res/service-fabric/cluster" + - "avm/res/service-networking/traffic-controller" - "avm/res/signal-r-service/signal-r" - "avm/res/signal-r-service/web-pub-sub" - "avm/res/sql/instance-pool" diff --git a/.github/workflows/avm.res.service-networking.traffic-controller.yml b/.github/workflows/avm.res.service-networking.traffic-controller.yml new file mode 100644 index 0000000000..775de7a740 --- /dev/null +++ b/.github/workflows/avm.res.service-networking.traffic-controller.yml @@ -0,0 +1,88 @@ +name: "avm.res.service-networking.traffic-controller" + +on: + workflow_dispatch: + inputs: + staticValidation: + type: boolean + description: "Execute static validation" + required: false + default: true + deploymentValidation: + type: boolean + description: "Execute deployment validation" + required: false + default: true + removeDeployment: + type: boolean + description: "Remove deployed module" + required: false + default: true + customLocation: + type: string + description: "Default location overwrite (e.g., eastus)" + required: false + push: + branches: + - main + paths: + - ".github/actions/templates/avm-**" + - ".github/workflows/avm.template.module.yml" + - ".github/workflows/avm.res.service-networking.traffic-controller.yml" + - "avm/res/service-networking/traffic-controller/**" + - "avm/utilities/pipelines/**" + - "!avm/utilities/pipelines/platform/**" + - "!*/**/README.md" + +env: + modulePath: "avm/res/service-networking/traffic-controller" + workflowPath: ".github/workflows/avm.res.service-networking.traffic-controller.yml" + +concurrency: + group: ${{ github.workflow }} + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-latest + name: "Initialize pipeline" + steps: + - name: "Checkout" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: "Set input parameters to output variables" + id: get-workflow-param + uses: ./.github/actions/templates/avm-getWorkflowInput + with: + workflowPath: "${{ env.workflowPath}}" + - name: "Get module test file paths" + id: get-module-test-file-paths + uses: ./.github/actions/templates/avm-getModuleTestFiles + with: + modulePath: "${{ env.modulePath }}" + outputs: + workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }} + moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }} + psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }} + modulePath: "${{ env.modulePath }}" + + ############################## + # Call reusable workflow # + ############################## + call-workflow-passing-data: + name: "Run" + permissions: + id-token: write # For OIDC + contents: write # For release tags + needs: + - job_initialize_pipeline + uses: ./.github/workflows/avm.template.module.yml + with: + workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}" + moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}" + psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}" + modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}" + secrets: inherit diff --git a/avm/res/service-networking/traffic-controller/README.md b/avm/res/service-networking/traffic-controller/README.md new file mode 100644 index 0000000000..ce5cb08d93 --- /dev/null +++ b/avm/res/service-networking/traffic-controller/README.md @@ -0,0 +1,769 @@ +# Application Gateway for Containers `[Microsoft.ServiceNetworking/trafficControllers]` + +This module deploys an Application Gateway for Containers + +## Navigation + +- [Resource Types](#Resource-Types) +- [Usage examples](#Usage-examples) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Notes](#Notes) +- [Data Collection](#Data-Collection) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | +| `Microsoft.Insights/diagnosticSettings` | [2021-05-01-preview](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Insights/2021-05-01-preview/diagnosticSettings) | +| `Microsoft.ServiceNetworking/trafficControllers` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ServiceNetworking/2023-11-01/trafficControllers) | +| `Microsoft.ServiceNetworking/trafficControllers/associations` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ServiceNetworking/2023-11-01/trafficControllers/associations) | +| `Microsoft.ServiceNetworking/trafficControllers/frontends` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ServiceNetworking/2023-11-01/trafficControllers/frontends) | + +## Usage examples + +The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository. + +>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order. + +>**Note**: To reference the module, please use the following syntax `br/public:avm/res/service-networking/traffic-controller:`. + +- [Using only defaults](#example-1-using-only-defaults) +- [Using large parameter set](#example-2-using-large-parameter-set) +- [WAF-aligned](#example-3-waf-aligned) + +### Example 1: _Using only defaults_ + +This instance deploys the module with the minimum set of required parameters. + + +

+ +via Bicep module + +```bicep +module trafficController 'br/public:avm/res/service-networking/traffic-controller:' = { + name: 'trafficControllerDeployment' + params: { + // Required parameters + name: 'sntcmin001' + // Non-required parameters + location: '' + } +} +``` + +
+

+ +

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

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

+ +via Bicep module + +```bicep +module trafficController 'br/public:avm/res/service-networking/traffic-controller:' = { + name: 'trafficControllerDeployment' + params: { + // Required parameters + name: 'sntcmax001' + // Non-required parameters + associations: [ + { + name: 'association1' + subnetResourceId: '' + } + ] + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + name: 'customSetting' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + frontends: [ + { + name: 'frontend1' + } + { + name: 'frontend2' + } + ] + location: '' + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + roleAssignments: [ + { + name: 'a6931c52-0b79-4fe9-ad3d-72188dfff379' + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'Owner' + } + { + name: '' + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + } + { + principalId: '' + principalType: 'ServicePrincipal' + roleDefinitionIdOrName: '' + } + ] + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "sntcmax001" + }, + // Non-required parameters + "associations": { + "value": [ + { + "name": "association1", + "subnetResourceId": "" + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "metricCategories": [ + { + "category": "AllMetrics" + } + ], + "name": "customSetting", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ] + }, + "frontends": { + "value": [ + { + "name": "frontend1" + }, + { + "name": "frontend2" + } + ] + }, + "location": { + "value": "" + }, + "lock": { + "value": { + "kind": "CanNotDelete", + "name": "myCustomLockName" + } + }, + "roleAssignments": { + "value": [ + { + "name": "a6931c52-0b79-4fe9-ad3d-72188dfff379", + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "Owner" + }, + { + "name": "", + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c" + }, + { + "principalId": "", + "principalType": "ServicePrincipal", + "roleDefinitionIdOrName": "" + } + ] + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

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

+ +via Bicep module + +```bicep +module trafficController 'br/public:avm/res/service-networking/traffic-controller:' = { + name: 'trafficControllerDeployment' + params: { + // Required parameters + name: 'sntcwaf001' + // Non-required parameters + associations: [ + { + name: 'association1' + subnetResourceId: '' + } + ] + diagnosticSettings: [ + { + eventHubAuthorizationRuleResourceId: '' + eventHubName: '' + storageAccountResourceId: '' + workspaceResourceId: '' + } + ] + frontends: [ + { + name: 'frontend1' + } + { + name: 'frontend2' + } + ] + location: '' + tags: { + Environment: 'Non-Prod' + 'hidden-title': 'This is visible in the resource name' + Role: 'DeploymentValidation' + } + } +} +``` + +
+

+ +

+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + // Required parameters + "name": { + "value": "sntcwaf001" + }, + // Non-required parameters + "associations": { + "value": [ + { + "name": "association1", + "subnetResourceId": "" + } + ] + }, + "diagnosticSettings": { + "value": [ + { + "eventHubAuthorizationRuleResourceId": "", + "eventHubName": "", + "storageAccountResourceId": "", + "workspaceResourceId": "" + } + ] + }, + "frontends": { + "value": [ + { + "name": "frontend1" + }, + { + "name": "frontend2" + } + ] + }, + "location": { + "value": "" + }, + "tags": { + "value": { + "Environment": "Non-Prod", + "hidden-title": "This is visible in the resource name", + "Role": "DeploymentValidation" + } + } + } +} +``` + +
+

+ +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Name of the Application Gateway for Containers to create. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`associations`](#parameter-associations) | array | List of Application Gateway for Containers associations. At this time, the number of associations is limited to 1. | +| [`diagnosticSettings`](#parameter-diagnosticsettings) | array | The diagnostic settings of the service. | +| [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | +| [`frontends`](#parameter-frontends) | array | List of Application Gateway for Containers frontends. | +| [`location`](#parameter-location) | string | Location for all Resources. | +| [`lock`](#parameter-lock) | object | The lock settings of the service. | +| [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | +| [`tags`](#parameter-tags) | object | Resource tags. | + +### Parameter: `name` + +Name of the Application Gateway for Containers to create. + +- Required: Yes +- Type: string + +### Parameter: `associations` + +List of Application Gateway for Containers associations. At this time, the number of associations is limited to 1. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-associationsname) | string | The name of the Application Gateway for Containers association. | +| [`subnetResourceId`](#parameter-associationssubnetresourceid) | string | The resource ID of the subnet to associate with the Application Gateway for Containers. | + +### Parameter: `associations.name` + +The name of the Application Gateway for Containers association. + +- Required: Yes +- Type: string + +### Parameter: `associations.subnetResourceId` + +The resource ID of the subnet to associate with the Application Gateway for Containers. + +- Required: Yes +- Type: string + +### Parameter: `diagnosticSettings` + +The diagnostic settings of the service. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventHubAuthorizationRuleResourceId`](#parameter-diagnosticsettingseventhubauthorizationruleresourceid) | string | Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. | +| [`eventHubName`](#parameter-diagnosticsettingseventhubname) | string | Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| [`logAnalyticsDestinationType`](#parameter-diagnosticsettingsloganalyticsdestinationtype) | string | A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. | +| [`logCategoriesAndGroups`](#parameter-diagnosticsettingslogcategoriesandgroups) | array | The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. | +| [`marketplacePartnerResourceId`](#parameter-diagnosticsettingsmarketplacepartnerresourceid) | string | The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. | +| [`metricCategories`](#parameter-diagnosticsettingsmetriccategories) | array | The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. | +| [`name`](#parameter-diagnosticsettingsname) | string | The name of diagnostic setting. | +| [`storageAccountResourceId`](#parameter-diagnosticsettingsstorageaccountresourceid) | string | Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | +| [`workspaceResourceId`](#parameter-diagnosticsettingsworkspaceresourceid) | string | Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. | + +### Parameter: `diagnosticSettings.eventHubAuthorizationRuleResourceId` + +Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.eventHubName` + +Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logAnalyticsDestinationType` + +A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'AzureDiagnostics' + 'Dedicated' + ] + ``` + +### Parameter: `diagnosticSettings.logCategoriesAndGroups` + +The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection. + +- Required: No +- Type: array + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingslogcategoriesandgroupscategory) | string | Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. | +| [`categoryGroup`](#parameter-diagnosticsettingslogcategoriesandgroupscategorygroup) | string | Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. | +| [`enabled`](#parameter-diagnosticsettingslogcategoriesandgroupsenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.category` + +Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.categoryGroup` + +Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.logCategoriesAndGroups.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.marketplacePartnerResourceId` + +The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.metricCategories` + +The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`category`](#parameter-diagnosticsettingsmetriccategoriescategory) | string | Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`enabled`](#parameter-diagnosticsettingsmetriccategoriesenabled) | bool | Enable or disable the category explicitly. Default is `true`. | + +### Parameter: `diagnosticSettings.metricCategories.category` + +Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics. + +- Required: Yes +- Type: string + +### Parameter: `diagnosticSettings.metricCategories.enabled` + +Enable or disable the category explicitly. Default is `true`. + +- Required: No +- Type: bool + +### Parameter: `diagnosticSettings.name` + +The name of diagnostic setting. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.storageAccountResourceId` + +Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `diagnosticSettings.workspaceResourceId` + +Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub. + +- Required: No +- Type: string + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `frontends` + +List of Application Gateway for Containers frontends. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-frontendsname) | string | The name of the Application Gateway for Containers frontend. | + +### Parameter: `frontends.name` + +The name of the Application Gateway for Containers frontend. + +- Required: Yes +- Type: string + +### Parameter: `location` + +Location for all Resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array +- Roles configurable by name: + - `'Contributor'` + - `'Owner'` + - `'Reader'` + - `'Role Based Access Control Administrator'` + - `'User Access Administrator'` + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". | +| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`name`](#parameter-roleassignmentsname) | string | The name (as GUID) of the role assignment. If not provided, a GUID will be generated. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". + +- Required: No +- Type: string + +### Parameter: `roleAssignments.conditionVersion` + +Version of the condition. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + '2.0' + ] + ``` + +### Parameter: `roleAssignments.delegatedManagedIdentityResourceId` + +The Resource Id of the delegated managed identity resource. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.description` + +The description of the role assignment. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.name` + +The name (as GUID) of the role assignment. If not provided, a GUID will be generated. + +- Required: No +- Type: string + +### Parameter: `roleAssignments.principalType` + +The principal type of the assigned principal ID. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' + ] + ``` + +### Parameter: `tags` + +Resource tags. + +- Required: No +- Type: object + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `associations` | array | The associations of the Application Gateway for Containers. | +| `configurationEndpoints` | array | The configuration endpoints of the Application Gateway for Containers. | +| `frontends` | array | The frontends of the Application Gateway for Containers. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The name of the Application Gateway for Containers. | +| `resourceGroupName` | string | The name of the resource group the resource was created in. | +| `resourceId` | string | The resource ID of the Application Gateway for Containers. | + +## Notes + +> **Limitation**: At this time, the number of associations is limited to 1 (Source: [Application Gateway for Containers associations](https://learn.microsoft.com/en-us/azure/application-gateway/for-containers/application-gateway-for-containers-components#application-gateway-for-containers-associations)) + +## Data Collection + +The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices. diff --git a/avm/res/service-networking/traffic-controller/association/README.md b/avm/res/service-networking/traffic-controller/association/README.md new file mode 100644 index 0000000000..87488abfe2 --- /dev/null +++ b/avm/res/service-networking/traffic-controller/association/README.md @@ -0,0 +1,79 @@ +# Application Gateway for Containers Association `[Microsoft.ServiceNetworking/trafficControllers/associations]` + +This module deploys an Application Gateway for Containers Association + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ServiceNetworking/trafficControllers/associations` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ServiceNetworking/2023-11-01/trafficControllers/associations) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Name of the association to create. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`trafficControllerName`](#parameter-trafficcontrollername) | string | The name of the parent Application Gateway for Containers instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`location`](#parameter-location) | string | Location for all Resources. | + +**Reuired parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`subnetResourceId`](#parameter-subnetresourceid) | string | The resource ID of the subnet to associate with the traffic controller. | + +### Parameter: `name` + +Name of the association to create. + +- Required: Yes +- Type: string + +### Parameter: `trafficControllerName` + +The name of the parent Application Gateway for Containers instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `location` + +Location for all Resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `subnetResourceId` + +The resource ID of the subnet to associate with the traffic controller. + +- Required: Yes +- Type: string + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the association. | +| `resourceGroupName` | string | The name of the resource group the resource was created in. | +| `resourceId` | string | The resource ID of the association. | +| `subnetResourceId` | string | The resource ID of the associated subnet. | diff --git a/avm/res/service-networking/traffic-controller/association/main.bicep b/avm/res/service-networking/traffic-controller/association/main.bicep new file mode 100644 index 0000000000..70cc047a29 --- /dev/null +++ b/avm/res/service-networking/traffic-controller/association/main.bicep @@ -0,0 +1,51 @@ +metadata name = 'Application Gateway for Containers Association' +metadata description = 'This module deploys an Application Gateway for Containers Association' +metadata owner = 'Azure/module-maintainers' + +@description('Required. Name of the association to create.') +param name string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Conditional. The name of the parent Application Gateway for Containers instance. Required if the template is used in a standalone deployment.') +param trafficControllerName string + +@description('Reuired. The resource ID of the subnet to associate with the traffic controller.') +param subnetResourceId string + +// ============== // +// Resources // +// ============== // + +resource trafficController 'Microsoft.ServiceNetworking/trafficControllers@2023-11-01' existing = { + name: trafficControllerName +} + +resource association 'Microsoft.ServiceNetworking/trafficControllers/associations@2023-11-01' = { + name: name + parent: trafficController + location: location + properties: { + associationType: 'subnets' + subnet: { + id: subnetResourceId + } + } +} + +// ============ // +// Outputs // +// ============ // + +@description('The resource ID of the association.') +output resourceId string = association.id + +@description('The name of the association.') +output name string = association.name + +@description('The name of the resource group the resource was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The resource ID of the associated subnet.') +output subnetResourceId string = association.properties.subnet.id diff --git a/avm/res/service-networking/traffic-controller/association/main.json b/avm/res/service-networking/traffic-controller/association/main.json new file mode 100644 index 0000000000..638bc670f4 --- /dev/null +++ b/avm/res/service-networking/traffic-controller/association/main.json @@ -0,0 +1,85 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.30.23.60470", + "templateHash": "17751059838446450774" + }, + "name": "Application Gateway for Containers Association", + "description": "This module deploys an Application Gateway for Containers Association", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the association to create." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "trafficControllerName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Application Gateway for Containers instance. Required if the template is used in a standalone deployment." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Reuired. The resource ID of the subnet to associate with the traffic controller." + } + } + }, + "resources": [ + { + "type": "Microsoft.ServiceNetworking/trafficControllers/associations", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('trafficControllerName'), parameters('name'))]", + "location": "[parameters('location')]", + "properties": { + "associationType": "subnets", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + } + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the association." + }, + "value": "[resourceId('Microsoft.ServiceNetworking/trafficControllers/associations', parameters('trafficControllerName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the association." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the resource was created in." + }, + "value": "[resourceGroup().name]" + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the associated subnet." + }, + "value": "[reference(resourceId('Microsoft.ServiceNetworking/trafficControllers/associations', parameters('trafficControllerName'), parameters('name')), '2023-11-01').subnet.id]" + } + } +} \ No newline at end of file diff --git a/avm/res/service-networking/traffic-controller/frontend/README.md b/avm/res/service-networking/traffic-controller/frontend/README.md new file mode 100644 index 0000000000..c9447a6ba3 --- /dev/null +++ b/avm/res/service-networking/traffic-controller/frontend/README.md @@ -0,0 +1,66 @@ +# Application Gateway for Containers Frontend `[Microsoft.ServiceNetworking/trafficControllers/frontends]` + +This module deploys an Application Gateway for Containers Frontend + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.ServiceNetworking/trafficControllers/frontends` | [2023-11-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.ServiceNetworking/2023-11-01/trafficControllers/frontends) | + +## Parameters + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-name) | string | Name of the frontend to create. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`trafficControllerName`](#parameter-trafficcontrollername) | string | The name of the parent Application Gateway for Containers instance. Required if the template is used in a standalone deployment. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`location`](#parameter-location) | string | Location for all Resources. | + +### Parameter: `name` + +Name of the frontend to create. + +- Required: Yes +- Type: string + +### Parameter: `trafficControllerName` + +The name of the parent Application Gateway for Containers instance. Required if the template is used in a standalone deployment. + +- Required: Yes +- Type: string + +### Parameter: `location` + +Location for all Resources. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +## Outputs + +| Output | Type | Description | +| :-- | :-- | :-- | +| `fqdn` | string | The FQDN of the frontend. | +| `name` | string | The name of the frontend. | +| `resourceGroupName` | string | The name of the resource group the resource was created in. | +| `resourceId` | string | The resource ID of the frontend. | diff --git a/avm/res/service-networking/traffic-controller/frontend/main.bicep b/avm/res/service-networking/traffic-controller/frontend/main.bicep new file mode 100644 index 0000000000..973eb532a7 --- /dev/null +++ b/avm/res/service-networking/traffic-controller/frontend/main.bicep @@ -0,0 +1,43 @@ +metadata name = 'Application Gateway for Containers Frontend' +metadata description = 'This module deploys an Application Gateway for Containers Frontend' +metadata owner = 'Azure/module-maintainers' + +@description('Required. Name of the frontend to create.') +param name string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Conditional. The name of the parent Application Gateway for Containers instance. Required if the template is used in a standalone deployment.') +param trafficControllerName string + +// ============== // +// Resources // +// ============== // + +resource trafficController 'Microsoft.ServiceNetworking/trafficControllers@2023-11-01' existing = { + name: trafficControllerName +} + +resource frontend 'Microsoft.ServiceNetworking/trafficControllers/frontends@2023-11-01' = { + name: name + parent: trafficController + location: location + properties: {} +} + +// ============ // +// Outputs // +// ============ // + +@description('The resource ID of the frontend.') +output resourceId string = frontend.id + +@description('The name of the frontend.') +output name string = frontend.name + +@description('The name of the resource group the resource was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The FQDN of the frontend.') +output fqdn string = frontend.properties.fqdn diff --git a/avm/res/service-networking/traffic-controller/frontend/main.json b/avm/res/service-networking/traffic-controller/frontend/main.json new file mode 100644 index 0000000000..f3fb57ad7e --- /dev/null +++ b/avm/res/service-networking/traffic-controller/frontend/main.json @@ -0,0 +1,74 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.30.23.60470", + "templateHash": "12093954076612099884" + }, + "name": "Application Gateway for Containers Frontend", + "description": "This module deploys an Application Gateway for Containers Frontend", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the frontend to create." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "trafficControllerName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Application Gateway for Containers instance. Required if the template is used in a standalone deployment." + } + } + }, + "resources": [ + { + "type": "Microsoft.ServiceNetworking/trafficControllers/frontends", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('trafficControllerName'), parameters('name'))]", + "location": "[parameters('location')]", + "properties": {} + } + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the frontend." + }, + "value": "[resourceId('Microsoft.ServiceNetworking/trafficControllers/frontends', parameters('trafficControllerName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the frontend." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the resource was created in." + }, + "value": "[resourceGroup().name]" + }, + "fqdn": { + "type": "string", + "metadata": { + "description": "The FQDN of the frontend." + }, + "value": "[reference(resourceId('Microsoft.ServiceNetworking/trafficControllers/frontends', parameters('trafficControllerName'), parameters('name')), '2023-11-01').fqdn]" + } + } +} \ No newline at end of file diff --git a/avm/res/service-networking/traffic-controller/main.bicep b/avm/res/service-networking/traffic-controller/main.bicep new file mode 100644 index 0000000000..2ac083b224 --- /dev/null +++ b/avm/res/service-networking/traffic-controller/main.bicep @@ -0,0 +1,301 @@ +metadata name = 'Application Gateway for Containers' +metadata description = 'This module deploys an Application Gateway for Containers' +metadata owner = 'Azure/module-maintainers' + +@description('Required. Name of the Application Gateway for Containers to create.') +param name string + +@description('Optional. Location for all Resources.') +param location string = resourceGroup().location + +@description('Optional. Enable/Disable usage telemetry for module.') +param enableTelemetry bool = true + +@description('Optional. Resource tags.') +param tags object? + +@description('Optional. The lock settings of the service.') +param lock lockType + +@description('Optional. The diagnostic settings of the service.') +param diagnosticSettings diagnosticSettingType + +@description('Optional. Array of role assignments to create.') +param roleAssignments roleAssignmentType + +@description('Optional. List of Application Gateway for Containers frontends.') +param frontends frontendType + +@description('Optional. List of Application Gateway for Containers associations. At this time, the number of associations is limited to 1.') +param associations associationType + +var builtInRoleNames = { + Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Role Based Access Control Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'f58310d9-a9f6-439a-9e8d-f62e7b41a168' + ) + 'User Access Administrator': subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9' + ) +} + +var formattedRoleAssignments = [ + for (roleAssignment, index) in (roleAssignments ?? []): union(roleAssignment, { + roleDefinitionId: builtInRoleNames[?roleAssignment.roleDefinitionIdOrName] ?? (contains( + roleAssignment.roleDefinitionIdOrName, + '/providers/Microsoft.Authorization/roleDefinitions/' + ) + ? roleAssignment.roleDefinitionIdOrName + : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName)) + }) +] + +// ============== // +// Resources // +// ============== // + +#disable-next-line no-deployments-resources +resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { + name: '46d3xbcp.res.servicenetworking-trafficcontroller.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + outputs: { + telemetry: { + type: 'String' + value: 'For more information, see https://aka.ms/avm/TelemetryInfo' + } + } + } + } +} + +resource trafficController 'Microsoft.ServiceNetworking/trafficControllers@2023-11-01' = { + name: name + location: location + tags: tags + properties: {} +} + +resource trafficController_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') { + name: lock.?name ?? 'lock-${name}' + properties: { + level: lock.?kind ?? '' + notes: lock.?kind == 'CanNotDelete' + ? 'Cannot delete resource or child resources.' + : 'Cannot delete or modify the resource or child resources.' + } + scope: trafficController +} + +resource trafficController_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = [ + for (diagnosticSetting, index) in (diagnosticSettings ?? []): { + name: diagnosticSetting.?name ?? '${name}-diagnosticSettings' + properties: { + storageAccountId: diagnosticSetting.?storageAccountResourceId + workspaceId: diagnosticSetting.?workspaceResourceId + eventHubAuthorizationRuleId: diagnosticSetting.?eventHubAuthorizationRuleResourceId + eventHubName: diagnosticSetting.?eventHubName + metrics: [ + for group in (diagnosticSetting.?metricCategories ?? [{ category: 'AllMetrics' }]): { + category: group.category + enabled: group.?enabled ?? true + timeGrain: null + } + ] + logs: [ + for group in (diagnosticSetting.?logCategoriesAndGroups ?? [{ categoryGroup: 'allLogs' }]): { + categoryGroup: group.?categoryGroup + category: group.?category + enabled: group.?enabled ?? true + } + ] + marketplacePartnerId: diagnosticSetting.?marketplacePartnerResourceId + logAnalyticsDestinationType: diagnosticSetting.?logAnalyticsDestinationType + } + scope: trafficController + } +] + +resource trafficController_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [ + for (roleAssignment, index) in (formattedRoleAssignments ?? []): { + name: roleAssignment.?name ?? guid( + trafficController.id, + roleAssignment.principalId, + roleAssignment.roleDefinitionId + ) + properties: { + roleDefinitionId: roleAssignment.roleDefinitionId + principalId: roleAssignment.principalId + description: roleAssignment.?description + principalType: roleAssignment.?principalType + condition: roleAssignment.?condition + conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set + delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId + } + scope: trafficController + } +] + +module trafficController_frontends 'frontend/main.bicep' = [ + for (frontend, index) in (frontends ?? []): { + name: '${uniqueString(deployment().name, location)}-TrafficController-Frontend-${index}' + params: { + trafficControllerName: trafficController.name + name: frontend.name + location: location + } + } +] + +module trafficController_associations 'association/main.bicep' = [ + for (association, index) in (associations ?? []): { + name: '${uniqueString(deployment().name, location)}-TrafficController-Association-${index}' + params: { + trafficControllerName: trafficController.name + name: association.name + location: location + subnetResourceId: association.subnetResourceId + } + } +] + +// ============ // +// Outputs // +// ============ // + +@description('The resource ID of the Application Gateway for Containers.') +output resourceId string = trafficController.id + +@description('The name of the Application Gateway for Containers.') +output name string = trafficController.name + +@description('The name of the resource group the resource was created in.') +output resourceGroupName string = resourceGroup().name + +@description('The location the resource was deployed into.') +output location string = trafficController.location + +@description('The configuration endpoints of the Application Gateway for Containers.') +output configurationEndpoints string[] = trafficController.properties.configurationEndpoints + +@description('The frontends of the Application Gateway for Containers.') +output frontends array = [ + for (frontend, i) in (!empty(frontends) ? array(frontends) : []): { + name: trafficController_frontends[i].outputs.name + resourceId: trafficController_frontends[i].outputs.resourceId + fqdn: trafficController_frontends[i].outputs.fqdn + } +] + +@description('The associations of the Application Gateway for Containers.') +output associations array = [ + for (association, i) in (!empty(associations) ? array(associations) : []): { + name: trafficController_associations[i].outputs.name + resourceId: trafficController_associations[i].outputs.resourceId + subnetResourceId: trafficController_associations[i].outputs.subnetResourceId + } +] + +// ================ // +// Definitions // +// ================ // + +type frontendType = { + @description('Required. The name of the Application Gateway for Containers frontend.') + name: string +}[]? + +@maxLength(1) +type associationType = { + @description('Required. The name of the Application Gateway for Containers association.') + name: string + + @description('Required. The resource ID of the subnet to associate with the Application Gateway for Containers.') + subnetResourceId: string +}[]? + +type lockType = { + @description('Optional. Specify the name of lock.') + name: string? + + @description('Optional. Specify the type of lock.') + kind: ('CanNotDelete' | 'ReadOnly' | 'None')? +}? + +type roleAssignmentType = { + @description('Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated.') + name: string? + + @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') + roleDefinitionIdOrName: string + + @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.') + principalId: string + + @description('Optional. The principal type of the assigned principal ID.') + principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')? + + @description('Optional. The description of the role assignment.') + description: string? + + @description('Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container".') + condition: string? + + @description('Optional. Version of the condition.') + conditionVersion: '2.0'? + + @description('Optional. The Resource Id of the delegated managed identity resource.') + delegatedManagedIdentityResourceId: string? +}[]? + +type diagnosticSettingType = { + @description('Optional. The name of diagnostic setting.') + name: string? + + @description('Optional. The name of logs that will be streamed. "allLogs" includes all possible logs for the resource. Set to `[]` to disable log collection.') + logCategoriesAndGroups: { + @description('Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here.') + category: string? + + @description('Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs.') + categoryGroup: string? + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @description('Optional. The name of metrics that will be streamed. "allMetrics" includes all possible metrics for the resource. Set to `[]` to disable metric collection.') + metricCategories: { + @description('Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics.') + category: string + + @description('Optional. Enable or disable the category explicitly. Default is `true`.') + enabled: bool? + }[]? + + @description('Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type.') + logAnalyticsDestinationType: ('Dedicated' | 'AzureDiagnostics')? + + @description('Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + workspaceResourceId: string? + + @description('Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + storageAccountResourceId: string? + + @description('Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to.') + eventHubAuthorizationRuleResourceId: string? + + @description('Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub.') + eventHubName: string? + + @description('Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs.') + marketplacePartnerResourceId: string? +}[]? diff --git a/avm/res/service-networking/traffic-controller/main.json b/avm/res/service-networking/traffic-controller/main.json new file mode 100644 index 0000000000..aa85cfc38c --- /dev/null +++ b/avm/res/service-networking/traffic-controller/main.json @@ -0,0 +1,742 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "languageVersion": "2.0", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.30.23.60470", + "templateHash": "13555357375349247834" + }, + "name": "Application Gateway for Containers", + "description": "This module deploys an Application Gateway for Containers", + "owner": "Azure/module-maintainers" + }, + "definitions": { + "frontendType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Application Gateway for Containers frontend." + } + } + } + }, + "nullable": true + }, + "associationType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the Application Gateway for Containers association." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of the subnet to associate with the Application Gateway for Containers." + } + } + } + }, + "nullable": true, + "maxLength": 1 + }, + "lockType": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the name of lock." + } + }, + "kind": { + "type": "string", + "allowedValues": [ + "CanNotDelete", + "None", + "ReadOnly" + ], + "nullable": true, + "metadata": { + "description": "Optional. Specify the type of lock." + } + } + }, + "nullable": true + }, + "roleAssignmentType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name (as GUID) of the role assignment. If not provided, a GUID will be generated." + } + }, + "roleDefinitionIdOrName": { + "type": "string", + "metadata": { + "description": "Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'." + } + }, + "principalId": { + "type": "string", + "metadata": { + "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to." + } + }, + "principalType": { + "type": "string", + "allowedValues": [ + "Device", + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ], + "nullable": true, + "metadata": { + "description": "Optional. The principal type of the assigned principal ID." + } + }, + "description": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The description of the role assignment." + } + }, + "condition": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase \"foo_storage_container\"." + } + }, + "conditionVersion": { + "type": "string", + "allowedValues": [ + "2.0" + ], + "nullable": true, + "metadata": { + "description": "Optional. Version of the condition." + } + }, + "delegatedManagedIdentityResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The Resource Id of the delegated managed identity resource." + } + } + } + }, + "nullable": true + }, + "diagnosticSettingType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of diagnostic setting." + } + }, + "logCategoriesAndGroups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category for a resource type this setting is applied to. Set the specific logs to collect here." + } + }, + "categoryGroup": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of a Diagnostic Log category group for a resource type this setting is applied to. Set to `allLogs` to collect all logs." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of logs that will be streamed. \"allLogs\" includes all possible logs for the resource. Set to `[]` to disable log collection." + } + }, + "metricCategories": { + "type": "array", + "items": { + "type": "object", + "properties": { + "category": { + "type": "string", + "metadata": { + "description": "Required. Name of a Diagnostic Metric category for a resource type this setting is applied to. Set to `AllMetrics` to collect all metrics." + } + }, + "enabled": { + "type": "bool", + "nullable": true, + "metadata": { + "description": "Optional. Enable or disable the category explicitly. Default is `true`." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. The name of metrics that will be streamed. \"allMetrics\" includes all possible metrics for the resource. Set to `[]` to disable metric collection." + } + }, + "logAnalyticsDestinationType": { + "type": "string", + "allowedValues": [ + "AzureDiagnostics", + "Dedicated" + ], + "nullable": true, + "metadata": { + "description": "Optional. A string indicating whether the export to Log Analytics should use the default destination type, i.e. AzureDiagnostics, or use a destination type." + } + }, + "workspaceResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic log analytics workspace. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "storageAccountResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic storage account. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "eventHubAuthorizationRuleResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of the diagnostic event hub authorization rule for the Event Hubs namespace in which the event hub should be created or streamed to." + } + }, + "eventHubName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Name of the diagnostic event hub within the namespace to which logs are streamed. Without this, an event hub is created for each log category. For security reasons, it is recommended to set diagnostic settings to send data to either storage account, log analytics workspace or event hub." + } + }, + "marketplacePartnerResourceId": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The full ARM resource ID of the Marketplace resource to which you would like to send Diagnostic Logs." + } + } + } + }, + "nullable": true + } + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the Application Gateway for Containers to create." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, + "tags": { + "type": "object", + "nullable": true, + "metadata": { + "description": "Optional. Resource tags." + } + }, + "lock": { + "$ref": "#/definitions/lockType", + "metadata": { + "description": "Optional. The lock settings of the service." + } + }, + "diagnosticSettings": { + "$ref": "#/definitions/diagnosticSettingType", + "metadata": { + "description": "Optional. The diagnostic settings of the service." + } + }, + "roleAssignments": { + "$ref": "#/definitions/roleAssignmentType", + "metadata": { + "description": "Optional. Array of role assignments to create." + } + }, + "frontends": { + "$ref": "#/definitions/frontendType", + "metadata": { + "description": "Optional. List of Application Gateway for Containers frontends." + } + }, + "associations": { + "$ref": "#/definitions/associationType", + "metadata": { + "description": "Optional. List of Application Gateway for Containers associations. At this time, the number of associations is limited to 1." + } + } + }, + "variables": { + "copy": [ + { + "name": "formattedRoleAssignments", + "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]", + "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" + } + ], + "builtInRoleNames": { + "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]", + "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", + "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", + "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" + } + }, + "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.servicenetworking-trafficcontroller.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, + "trafficController": { + "type": "Microsoft.ServiceNetworking/trafficControllers", + "apiVersion": "2023-11-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": {} + }, + "trafficController_lock": { + "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", + "type": "Microsoft.Authorization/locks", + "apiVersion": "2020-05-01", + "scope": "[format('Microsoft.ServiceNetworking/trafficControllers/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", + "properties": { + "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", + "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]" + }, + "dependsOn": [ + "trafficController" + ] + }, + "trafficController_diagnosticSettings": { + "copy": { + "name": "trafficController_diagnosticSettings", + "count": "[length(coalesce(parameters('diagnosticSettings'), createArray()))]" + }, + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.ServiceNetworking/trafficControllers/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", + "properties": { + "copy": [ + { + "name": "metrics", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics'))))]", + "input": { + "category": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')].category]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'metricCategories'), createArray(createObject('category', 'AllMetrics')))[copyIndex('metrics')], 'enabled'), true())]", + "timeGrain": null + } + }, + { + "name": "logs", + "count": "[length(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs'))))]", + "input": { + "categoryGroup": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'categoryGroup')]", + "category": "[tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'category')]", + "enabled": "[coalesce(tryGet(coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logCategoriesAndGroups'), createArray(createObject('categoryGroup', 'allLogs')))[copyIndex('logs')], 'enabled'), true())]" + } + } + ], + "storageAccountId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'storageAccountResourceId')]", + "workspaceId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'workspaceResourceId')]", + "eventHubAuthorizationRuleId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubAuthorizationRuleResourceId')]", + "eventHubName": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'eventHubName')]", + "marketplacePartnerId": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'marketplacePartnerResourceId')]", + "logAnalyticsDestinationType": "[tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'logAnalyticsDestinationType')]" + }, + "dependsOn": [ + "trafficController" + ] + }, + "trafficController_roleAssignments": { + "copy": { + "name": "trafficController_roleAssignments", + "count": "[length(coalesce(variables('formattedRoleAssignments'), createArray()))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.ServiceNetworking/trafficControllers/{0}', parameters('name'))]", + "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.ServiceNetworking/trafficControllers', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", + "properties": { + "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", + "principalId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId]", + "description": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'description')]", + "principalType": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'principalType')]", + "condition": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition')]", + "conditionVersion": "[if(not(empty(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]", + "delegatedManagedIdentityResourceId": "[tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]" + }, + "dependsOn": [ + "trafficController" + ] + }, + "trafficController_frontends": { + "copy": { + "name": "trafficController_frontends", + "count": "[length(coalesce(parameters('frontends'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-TrafficController-Frontend-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "trafficControllerName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('frontends'), createArray())[copyIndex()].name]" + }, + "location": { + "value": "[parameters('location')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.30.23.60470", + "templateHash": "12093954076612099884" + }, + "name": "Application Gateway for Containers Frontend", + "description": "This module deploys an Application Gateway for Containers Frontend", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the frontend to create." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "trafficControllerName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Application Gateway for Containers instance. Required if the template is used in a standalone deployment." + } + } + }, + "resources": [ + { + "type": "Microsoft.ServiceNetworking/trafficControllers/frontends", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('trafficControllerName'), parameters('name'))]", + "location": "[parameters('location')]", + "properties": {} + } + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the frontend." + }, + "value": "[resourceId('Microsoft.ServiceNetworking/trafficControllers/frontends', parameters('trafficControllerName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the frontend." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the resource was created in." + }, + "value": "[resourceGroup().name]" + }, + "fqdn": { + "type": "string", + "metadata": { + "description": "The FQDN of the frontend." + }, + "value": "[reference(resourceId('Microsoft.ServiceNetworking/trafficControllers/frontends', parameters('trafficControllerName'), parameters('name')), '2023-11-01').fqdn]" + } + } + } + }, + "dependsOn": [ + "trafficController" + ] + }, + "trafficController_associations": { + "copy": { + "name": "trafficController_associations", + "count": "[length(coalesce(parameters('associations'), createArray()))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('{0}-TrafficController-Association-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "trafficControllerName": { + "value": "[parameters('name')]" + }, + "name": { + "value": "[coalesce(parameters('associations'), createArray())[copyIndex()].name]" + }, + "location": { + "value": "[parameters('location')]" + }, + "subnetResourceId": { + "value": "[coalesce(parameters('associations'), createArray())[copyIndex()].subnetResourceId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.30.23.60470", + "templateHash": "17751059838446450774" + }, + "name": "Application Gateway for Containers Association", + "description": "This module deploys an Application Gateway for Containers Association", + "owner": "Azure/module-maintainers" + }, + "parameters": { + "name": { + "type": "string", + "metadata": { + "description": "Required. Name of the association to create." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Optional. Location for all Resources." + } + }, + "trafficControllerName": { + "type": "string", + "metadata": { + "description": "Conditional. The name of the parent Application Gateway for Containers instance. Required if the template is used in a standalone deployment." + } + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "Reuired. The resource ID of the subnet to associate with the traffic controller." + } + } + }, + "resources": [ + { + "type": "Microsoft.ServiceNetworking/trafficControllers/associations", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', parameters('trafficControllerName'), parameters('name'))]", + "location": "[parameters('location')]", + "properties": { + "associationType": "subnets", + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + } + } + ], + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the association." + }, + "value": "[resourceId('Microsoft.ServiceNetworking/trafficControllers/associations', parameters('trafficControllerName'), parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the association." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the resource was created in." + }, + "value": "[resourceGroup().name]" + }, + "subnetResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the associated subnet." + }, + "value": "[reference(resourceId('Microsoft.ServiceNetworking/trafficControllers/associations', parameters('trafficControllerName'), parameters('name')), '2023-11-01').subnet.id]" + } + } + } + }, + "dependsOn": [ + "trafficController" + ] + } + }, + "outputs": { + "resourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Application Gateway for Containers." + }, + "value": "[resourceId('Microsoft.ServiceNetworking/trafficControllers', parameters('name'))]" + }, + "name": { + "type": "string", + "metadata": { + "description": "The name of the Application Gateway for Containers." + }, + "value": "[parameters('name')]" + }, + "resourceGroupName": { + "type": "string", + "metadata": { + "description": "The name of the resource group the resource was created in." + }, + "value": "[resourceGroup().name]" + }, + "location": { + "type": "string", + "metadata": { + "description": "The location the resource was deployed into." + }, + "value": "[reference('trafficController', '2023-11-01', 'full').location]" + }, + "configurationEndpoints": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "The configuration endpoints of the Application Gateway for Containers." + }, + "value": "[reference('trafficController').configurationEndpoints]" + }, + "frontends": { + "type": "array", + "metadata": { + "description": "The frontends of the Application Gateway for Containers." + }, + "copy": { + "count": "[length(if(not(empty(parameters('frontends'))), array(parameters('frontends')), createArray()))]", + "input": { + "name": "[reference(format('trafficController_frontends[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('trafficController_frontends[{0}]', copyIndex())).outputs.resourceId.value]", + "fqdn": "[reference(format('trafficController_frontends[{0}]', copyIndex())).outputs.fqdn.value]" + } + } + }, + "associations": { + "type": "array", + "metadata": { + "description": "The associations of the Application Gateway for Containers." + }, + "copy": { + "count": "[length(if(not(empty(parameters('associations'))), array(parameters('associations')), createArray()))]", + "input": { + "name": "[reference(format('trafficController_associations[{0}]', copyIndex())).outputs.name.value]", + "resourceId": "[reference(format('trafficController_associations[{0}]', copyIndex())).outputs.resourceId.value]", + "subnetResourceId": "[reference(format('trafficController_associations[{0}]', copyIndex())).outputs.subnetResourceId.value]" + } + } + } + } +} \ No newline at end of file diff --git a/avm/res/service-networking/traffic-controller/tests/e2e/defaults/main.test.bicep b/avm/res/service-networking/traffic-controller/tests/e2e/defaults/main.test.bicep new file mode 100644 index 0000000000..3204196c73 --- /dev/null +++ b/avm/res/service-networking/traffic-controller/tests/e2e/defaults/main.test.bicep @@ -0,0 +1,48 @@ +targetScope = 'subscription' + +metadata name = 'Using only defaults' +metadata description = 'This instance deploys the module with the minimum set of required parameters.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-servicenetworking-trafficcontrollers-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'sntcmin' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { + name: resourceGroupName + location: resourceLocation +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + } + } +] diff --git a/avm/res/service-networking/traffic-controller/tests/e2e/max/dependencies.bicep b/avm/res/service-networking/traffic-controller/tests/e2e/max/dependencies.bicep new file mode 100644 index 0000000000..ee481bbc78 --- /dev/null +++ b/avm/res/service-networking/traffic-controller/tests/e2e/max/dependencies.bicep @@ -0,0 +1,49 @@ +@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 Virtual Network to create.') +param virtualNetworkName string + +var addressPrefix = '10.0.0.0/16' + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: managedIdentityName + location: location +} + +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2024-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 0) + delegations: [ + { + name: 'Microsoft.ServiceNetworking.trafficControllers' + properties: { + serviceName: 'Microsoft.ServiceNetworking/trafficControllers' + } + } + ] + } + } + ] + } +} + +@description('The resource ID of the created Managed Identity.') +output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created default Virtual Network Subnet.') +output defaultSubnetResourceId string = virtualNetwork.properties.subnets[0].id diff --git a/avm/res/service-networking/traffic-controller/tests/e2e/max/main.test.bicep b/avm/res/service-networking/traffic-controller/tests/e2e/max/main.test.bicep new file mode 100644 index 0000000000..3636eeb929 --- /dev/null +++ b/avm/res/service-networking/traffic-controller/tests/e2e/max/main.test.bicep @@ -0,0 +1,131 @@ +targetScope = 'subscription' + +metadata name = 'Using large parameter set' +metadata description = 'This instance deploys the module with most of its features enabled.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-servicenetworking-trafficcontrollers-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'sntcmax' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2024-03-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + frontends: [ + { + name: 'frontend1' + } + { + name: 'frontend2' + } + ] + associations: [ + { + name: 'association1' + subnetResourceId: nestedDependencies.outputs.defaultSubnetResourceId + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + lock: { + kind: 'CanNotDelete' + name: 'myCustomLockName' + } + roleAssignments: [ + { + name: 'a6931c52-0b79-4fe9-ad3d-72188dfff379' + roleDefinitionIdOrName: 'Owner' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + name: guid('Custom seed ${namePrefix}${serviceShort}') + roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + { + roleDefinitionIdOrName: subscriptionResourceId( + 'Microsoft.Authorization/roleDefinitions', + 'acdd72a7-3385-48ef-bd42-f606fba81ae7' + ) + principalId: nestedDependencies.outputs.managedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + ] + diagnosticSettings: [ + { + name: 'customSetting' + metricCategories: [ + { + category: 'AllMetrics' + } + ] + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + } + } +] diff --git a/avm/res/service-networking/traffic-controller/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/service-networking/traffic-controller/tests/e2e/waf-aligned/dependencies.bicep new file mode 100644 index 0000000000..c71e8b7ac8 --- /dev/null +++ b/avm/res/service-networking/traffic-controller/tests/e2e/waf-aligned/dependencies.bicep @@ -0,0 +1,38 @@ +@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@2024-01-01' = { + name: virtualNetworkName + location: location + properties: { + addressSpace: { + addressPrefixes: [ + addressPrefix + ] + } + subnets: [ + { + name: 'defaultSubnet' + properties: { + addressPrefix: cidrSubnet(addressPrefix, 24, 0) + delegations: [ + { + name: 'Microsoft.ServiceNetworking.trafficControllers' + properties: { + serviceName: 'Microsoft.ServiceNetworking/trafficControllers' + } + } + ] + } + } + ] + } +} + +@description('The resource ID of the created default Virtual Network Subnet.') +output defaultSubnetResourceId string = virtualNetwork.properties.subnets[0].id diff --git a/avm/res/service-networking/traffic-controller/tests/e2e/waf-aligned/main.test.bicep b/avm/res/service-networking/traffic-controller/tests/e2e/waf-aligned/main.test.bicep new file mode 100644 index 0000000000..499b6a760a --- /dev/null +++ b/avm/res/service-networking/traffic-controller/tests/e2e/waf-aligned/main.test.bicep @@ -0,0 +1,98 @@ +targetScope = 'subscription' + +metadata name = 'WAF-aligned' +metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.' + +// ========== // +// Parameters // +// ========== // + +@description('Optional. The name of the resource group to deploy for testing purposes.') +@maxLength(90) +param resourceGroupName string = 'dep-${namePrefix}-servicenetworking-trafficcontrollers-${serviceShort}-rg' + +@description('Optional. The location to deploy resources to.') +param resourceLocation string = deployment().location + +@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.') +param serviceShort string = 'sntcwaf' + +@description('Optional. A token to inject into the name of each resource. This value can be automatically injected by the CI.') +param namePrefix string = '#_namePrefix_#' + +// ============ // +// Dependencies // +// ============ // + +// General resources +// ================= +resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: resourceGroupName + location: resourceLocation +} + +module nestedDependencies 'dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-nestedDependencies' + params: { + location: resourceLocation + virtualNetworkName: 'dep-${namePrefix}-vnet-${serviceShort}' + } +} + +// Diagnostics +// =========== +module diagnosticDependencies '../../../../../../utilities/e2e-template-assets/templates/diagnostic.dependencies.bicep' = { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-diagnosticDependencies' + params: { + storageAccountName: 'dep${namePrefix}diasa${serviceShort}01' + logAnalyticsWorkspaceName: 'dep-${namePrefix}-law-${serviceShort}' + eventHubNamespaceEventHubName: 'dep-${namePrefix}-evh-${serviceShort}' + eventHubNamespaceName: 'dep-${namePrefix}-evhns-${serviceShort}' + location: resourceLocation + } +} + +// ============== // +// Test Execution // +// ============== // + +@batchSize(1) +module testDeployment '../../../main.bicep' = [ + for iteration in ['init', 'idem']: { + scope: resourceGroup + name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}' + params: { + name: '${namePrefix}${serviceShort}001' + location: resourceLocation + frontends: [ + { + name: 'frontend1' + } + { + name: 'frontend2' + } + ] + associations: [ + { + name: 'association1' + subnetResourceId: nestedDependencies.outputs.defaultSubnetResourceId + } + ] + tags: { + 'hidden-title': 'This is visible in the resource name' + Environment: 'Non-Prod' + Role: 'DeploymentValidation' + } + diagnosticSettings: [ + { + eventHubName: diagnosticDependencies.outputs.eventHubNamespaceEventHubName + eventHubAuthorizationRuleResourceId: diagnosticDependencies.outputs.eventHubAuthorizationRuleId + storageAccountResourceId: diagnosticDependencies.outputs.storageAccountResourceId + workspaceResourceId: diagnosticDependencies.outputs.logAnalyticsWorkspaceResourceId + } + ] + } + } +] diff --git a/avm/res/service-networking/traffic-controller/version.json b/avm/res/service-networking/traffic-controller/version.json new file mode 100644 index 0000000000..8def869ede --- /dev/null +++ b/avm/res/service-networking/traffic-controller/version.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", + "version": "0.1", + "pathFilters": [ + "./main.json" + ] +} From bea4f7ee5cd8e05c00829f989ec443f3a39f9ce5 Mon Sep 17 00:00:00 2001 From: "Axel B. Andersen" Date: Thu, 3 Oct 2024 10:15:27 +0200 Subject: [PATCH 57/57] fix: isHnsEnabled - `avm/res/storage/storage-account` (#3317) ## Description Fix Cannot redeploy if EnableHierargicalNamespace is false Fixes #1522 ## Pipeline Reference | Pipeline | | -------- | | [![avm.res.storage.storage-account](https://github.com/Agazoth/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml/badge.svg?branch=Agazoth%2FFixRedeployWithisHnsEnabled)](https://github.com/Agazoth/bicep-registry-modules/actions/workflows/avm.res.storage.storage-account.yml) | ## Type of Change - [ ] Update to CI Environment or utilities (Non-module affecting changes) - [x] Azure Verified Module updates: - [x] Bugfix containing backwards-compatible bug fixes, and I have NOT bumped the MAJOR or MINOR version in `version.json`: - [ ] Someone has opened a bug report issue, and I have included "Closes #{bug_report_issue_number}" in the PR description. - [ ] The bug was found by the module author, and no one has opened an issue to report it yet. - [ ] Feature update backwards compatible feature updates, and I have bumped the MINOR version in `version.json`. - [ ] Breaking changes and I have bumped the MAJOR version in `version.json`. - [ ] Update to documentation ## Checklist - [x] I'm sure there are no other open Pull Requests for the same update/change - [x] I have run `Set-AVMModule` locally to generate the supporting module files. - [x] My corresponding pipelines / checks run clean and green without any errors or warnings --- avm/res/storage/storage-account/main.bicep | 2 +- avm/res/storage/storage-account/main.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/avm/res/storage/storage-account/main.bicep b/avm/res/storage/storage-account/main.bicep index b8b321d82c..8e491ac177 100644 --- a/avm/res/storage/storage-account/main.bicep +++ b/avm/res/storage/storage-account/main.bicep @@ -412,7 +412,7 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2023-05-01' = { } : null supportsHttpsTrafficOnly: supportsHttpsTrafficOnly - isHnsEnabled: enableHierarchicalNamespace ? enableHierarchicalNamespace : null + isHnsEnabled: enableHierarchicalNamespace isSftpEnabled: enableSftp isNfsV3Enabled: enableNfsV3 ? enableNfsV3 : any('') largeFileSharesState: (skuName == 'Standard_LRS') || (skuName == 'Standard_ZRS') ? largeFileSharesState : null diff --git a/avm/res/storage/storage-account/main.json b/avm/res/storage/storage-account/main.json index 3f89f10c57..b6d1cc5d74 100644 --- a/avm/res/storage/storage-account/main.json +++ b/avm/res/storage/storage-account/main.json @@ -1063,7 +1063,7 @@ "accessTier": "[if(and(not(equals(parameters('kind'), 'Storage')), not(equals(parameters('kind'), 'BlockBlobStorage'))), parameters('accessTier'), null())]", "sasPolicy": "[if(not(empty(parameters('sasExpirationPeriod'))), createObject('expirationAction', 'Log', 'sasExpirationPeriod', parameters('sasExpirationPeriod')), null())]", "supportsHttpsTrafficOnly": "[parameters('supportsHttpsTrafficOnly')]", - "isHnsEnabled": "[if(parameters('enableHierarchicalNamespace'), parameters('enableHierarchicalNamespace'), null())]", + "isHnsEnabled": "[parameters('enableHierarchicalNamespace')]", "isSftpEnabled": "[parameters('enableSftp')]", "isNfsV3Enabled": "[if(parameters('enableNfsV3'), parameters('enableNfsV3'), '')]", "largeFileSharesState": "[if(or(equals(parameters('skuName'), 'Standard_LRS'), equals(parameters('skuName'), 'Standard_ZRS')), parameters('largeFileSharesState'), null())]",

Changelog

Sourced from github/codeql-action's changelog.

CodeQL Action Changelog

See the releases page for the relevant changes to the CodeQL CLI and language packs.

Note that the only difference between v2 and v3 of the CodeQL Action is the node version they support, with v3 running on node 20 while we continue to release v2 to support running on node 16. For example 3.22.11 was the first v3 release and is functionally identical to 2.22.11. This approach ensures an easy way to track exactly which features are included in different versions, indicated by the minor and patch version numbers.

[UNRELEASED]

No user facing changes.

3.26.9 - 24 Sep 2024

No user facing changes.

3.26.8 - 19 Sep 2024

  • Update default CodeQL bundle version to 2.19.0. #2483

3.26.7 - 13 Sep 2024

  • Update default CodeQL bundle version to 2.18.4. #2471

3.26.6 - 29 Aug 2024

  • Update default CodeQL bundle version to 2.18.3. #2449

3.26.5 - 23 Aug 2024

  • Fix an issue where the csrutil system call used for telemetry would fail on MacOS ARM machines with System Integrity Protection disabled. #2441

3.26.4 - 21 Aug 2024

  • Deprecation: The add-snippets input on the analyze Action is deprecated and will be removed in the first release in August 2025. #2436
  • Fix an issue where the disk usage system call used for telemetry would fail on MacOS ARM machines with System Integrity Protection disabled, and then surface a warning. The system call is now disabled for these machines. #2434

3.26.3 - 19 Aug 2024

  • Fix an issue where the CodeQL Action could not write diagnostic messages on Windows. This issue did not impact analysis quality. #2430

3.26.2 - 14 Aug 2024

  • Update default CodeQL bundle version to 2.18.2. #2417

3.26.1 - 13 Aug 2024

No user facing changes.

3.26.0 - 06 Aug 2024

  • Deprecation: Swift analysis on Ubuntu runner images is no longer supported. Please migrate to a macOS runner if this affects you. #2403

... (truncated)