Skip to content

Commit 596c3e1

Browse files
committed
New deployment script article
1 parent 93668dd commit 596c3e1

File tree

1 file changed

+205
-0
lines changed

1 file changed

+205
-0
lines changed
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
---
2+
title: Access a private virtual network from a Bicep deployment script
3+
description: Learn how to run and test Bicep deployment scripts in private networks.
4+
ms.custom: devx-track-bicep
5+
ms.topic: how-to
6+
ms.date: 12/13/2023
7+
---
8+
9+
# Run Bicep deployment script privately over a private endpoint
10+
11+
With the `2023-08-01` API version of the `Microsoft.Resources/deploymentScripts` resource it is possible to run deployment scripts privately in an Azure Container Instance.
12+
13+
This means that the Azure Container Instance created by the deployment script resource is running in a virtual network and is assigned a private IP address. The Azure Container Instance connects to a new or existing storage account over a private endpoint.
14+
15+
The `2023-08-01` API version introduces the `subnetIds` property under `containerSettings` to specify that the Azure Container Instance must be deployed in a subnet in the virtual network.
16+
17+
:::image type="content" source="./media/deployment-script-vnet-pe/bicep-deployment-script-pe-design.png" alt-text="Screenshot of high-level architecture showing how the infrastructure is connected to run deployment scripts privately.":::
18+
19+
To run deployment scripts privately you need the following infrastructure as seen in the architecture image above:
20+
21+
- Create a virtual network with two subnets:
22+
- Subnet for private endpoint
23+
- Subnet for Azure Container Instance, this subnet needs a `Microsoft.ContainerInstance/containerGroups` delegation.
24+
- Create a storage account with public network access `disabled`
25+
- Create a private endpoint configured with the `file` sub-resource on the storage account
26+
- Create a private DNS zone `privatelink.file.core.windows.net` and register the private endpoint IP address as an A record. Link the private DNS zone to the created virtual network.
27+
- Create a user-assigned managed identity with `Storage File Data Privileged Contributor` permissions on the storage account and specify it in the `identity` property in the deployment script resource. To assign the identity, see [Identity](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/deployment-script-develop#identity).
28+
29+
The Azure Container Instance is deployed implicitly by the deployment script resource.
30+
31+
The following Bicep template shows the Bicep code needed to configure the infrastructure required for running a deployment script privately:
32+
33+
```bicep
34+
@maxLength(10) // Required maximum length, because the storage account has a maximum of 26 characters
35+
param prefix string
36+
param location string = resourceGroup().location
37+
param userAssignedIdentityName string = '${prefix}Identity'
38+
param storageAccountName string = '${prefix}stg${uniqueString(resourceGroup().id)}'
39+
param vnetName string = '${prefix}Vnet'
40+
param deploymentScriptName string = '${prefix}ds'
41+
42+
resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
43+
name: userAssignedIdentityName
44+
location: location
45+
}
46+
47+
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
48+
name: storageAccountName
49+
kind: 'StorageV2'
50+
location: location
51+
sku: {
52+
name: 'Standard_LRS'
53+
}
54+
properties: {
55+
publicNetworkAccess: 'Disabled'
56+
networkAcls: {
57+
defaultAction: 'Deny'
58+
bypass: 'AzureServices'
59+
}
60+
}
61+
}
62+
63+
resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-05-01' = {
64+
name: storageAccount.name
65+
location: location
66+
properties: {
67+
privateLinkServiceConnections: [
68+
{
69+
name: storageAccount.name
70+
properties: {
71+
privateLinkServiceId: storageAccount.id
72+
groupIds: [
73+
'file'
74+
]
75+
}
76+
}
77+
]
78+
customNetworkInterfaceName: '${storageAccount.name}-nic'
79+
subnet: {
80+
id: virtualNetwork::privateEndpointSubnet.id
81+
}
82+
}
83+
}
84+
85+
resource storageFileDataPrivilegedContributorReference 'Microsoft.Authorization/roleDefinitions@2022-04-01' existing = {
86+
name: '69566ab7-960f-475b-8e7c-b3118f30c6bd' // Storage File Data Privileged Contributor
87+
scope: tenant()
88+
}
89+
90+
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
91+
name: guid(storageFileDataPrivilegedContributorReference.id, managedIdentity.id, storageAccount.id)
92+
scope: storageAccount
93+
properties: {
94+
principalId: managedIdentity.properties.principalId
95+
roleDefinitionId: storageFileDataPrivilegedContributorReference.id
96+
principalType: 'ServicePrincipal'
97+
}
98+
}
99+
100+
resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
101+
name: 'privatelink.file.core.windows.net'
102+
location: 'global'
103+
104+
resource virtualNetworkLink 'virtualNetworkLinks' = {
105+
name: uniqueString(virtualNetwork.name)
106+
location: 'global'
107+
properties: {
108+
registrationEnabled: false
109+
virtualNetwork: {
110+
id: virtualNetwork.id
111+
}
112+
}
113+
}
114+
115+
resource resRecord 'A' = {
116+
name: storageAccount.name
117+
properties: {
118+
ttl: 10
119+
aRecords: [
120+
{
121+
ipv4Address: first(first(privateEndpoint.properties.customDnsConfigs)!.ipAddresses)
122+
}
123+
]
124+
}
125+
}
126+
}
127+
128+
resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-05-01' = {
129+
name: vnetName
130+
location: location
131+
properties:{
132+
addressSpace: {
133+
addressPrefixes: [
134+
'192.168.4.0/23'
135+
]
136+
}
137+
}
138+
139+
resource privateEndpointSubnet 'subnets' = {
140+
name: 'PrivateEndpointSubnet'
141+
properties: {
142+
addressPrefixes: [
143+
'192.168.4.0/24'
144+
]
145+
}
146+
}
147+
148+
resource containerInstanceSubnet 'subnets' = {
149+
name: 'ContainerInstanceSubnet'
150+
properties: {
151+
addressPrefix: '192.168.5.0/24'
152+
delegations: [
153+
{
154+
name: 'containerDelegation'
155+
properties: {
156+
serviceName: 'Microsoft.ContainerInstance/containerGroups'
157+
}
158+
}
159+
]
160+
}
161+
}
162+
}
163+
164+
resource privateDeploymentScript 'Microsoft.Resources/deploymentScripts@2023-08-01' = {
165+
name: deploymentScriptName
166+
dependsOn: [
167+
privateEndpoint
168+
privateDnsZone::virtualNetworkLink
169+
]
170+
location: location
171+
kind: 'AzurePowerShell'
172+
identity: {
173+
type: 'UserAssigned'
174+
userAssignedIdentities: {
175+
'${managedIdentity.id}' : {}
176+
}
177+
}
178+
properties: {
179+
storageAccountSettings: {
180+
storageAccountName: storageAccount.name
181+
}
182+
containerSettings: {
183+
subnetIds: [
184+
{
185+
id: virtualNetwork::containerInstanceSubnet.id
186+
}
187+
]
188+
}
189+
azPowerShellVersion: '9.0'
190+
retentionInterval: 'P1D'
191+
scriptContent: 'Write-Host "Hello World!"'
192+
}
193+
}
194+
```
195+
196+
## Firewall
197+
198+
The Azure Container Instance downloads container images from the Microsoft Container Registry. If you make use of a firewall whitelist the URL [mcr.microsoft.com](http://mcr.microsoft.com) to download the image successfully. If the container image cannot be downloaded it will go into a `waiting` state and will eventually throw a timeout error.
199+
200+
## Next steps
201+
202+
In this article, you learned how to run deployment scripts over a private endpoint. To learn more:
203+
204+
> [!div class="nextstepaction"]
205+
> [Use deployment scripts in Bicep](./deployment-script-bicep.md)

0 commit comments

Comments
 (0)