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 01/68] 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 02/68] 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 03/68] 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 04/68] 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 05/68] 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 06/68] 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: ''
+ }
+}
+```
+
+
+
+
+## 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 |
+|--|--|--|
+| | Resource Group | The resource group hosting the image resources |
+| | (Image) Resource Group | The resource group hosting the resources created during the image build |
+| | (Assets) Storage Account | The storage account that hosts the image customization scripts used by the _Azure Image Building_ when executing the image template. |
+| | (DS) Storage Account | The storage account that hosts the files of the Deployment Scripts. Required for private networking. |
+| | (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. |
+| | (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. |
+| | (Storage) Deployment Script | The Deployment Script that uploads the customization scripts to the Assets Storage Account. |
+| | (Trigger) Deployment Script | The Deployment Script that triggers the Image Template build. |
+| | 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 | 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 | 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 | 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. |
+
+
+
+
+
+### 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^T?6?qO0~xar{|KS0h|I5q81$gTO=M>;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{5eQra9z3wTsPuB{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%p2U4$lYT0lxz}>?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 @@
+
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 @@
+
\ 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 @@
+
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 @@
+
\ 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=5aO}*>jMr)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~Kv2}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{_hjHr}(^LDU6=3bgQ_-c=KH~weK(;&@)fHv37(kw6H%&)aRMS6=cbXOM
zTd`n-ZB?7l8;O52Evii%=>+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`032b12pzRc`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