Skip to content

Commit 1f1d843

Browse files
Niall LangleyNJLangley
authored andcommitted
Added functions app to ARM template and added functions SPN permissions to access the data lake to the ARM template
1 parent 6cd465e commit 1f1d843

File tree

2 files changed

+226
-11
lines changed

2 files changed

+226
-11
lines changed

.github/workflows/test_azure_devtest_labs_integration.yml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,22 @@ jobs:
6464
BRANCH_NAME=${GITHUB_REF#*refs/heads/}
6565
echo "Branch Name: $BRANCH_NAME"
6666
67+
# This is the object id of the Enterprise Application created from the App Registration
68+
69+
echo "Retriving service principal id for the logged in user..."
70+
SERVICEPRINCIPALAPPID=$(az account show | jq --raw-output '.user.name')
71+
echo "Service Principal App Id: $SERVICEPRINCIPALAPPID"
72+
73+
SERVICEPRINCIPALID=$( az ad sp list --filter "appId eq '$SERVICEPRINCIPALAPPID' and servicePrincipalType eq 'Application'" --query [0].objectId --output tsv)
74+
echo "Service Principal Object Id: $SERVICEPRINCIPALID"
75+
76+
echo "Building parameters file for ARM deployment..."
6777
PARAMETERS_FILE="$(pwd)/azuredeploy.parameters.json"
68-
echo $'[ { "name":"'branch'", "value":"'$BRANCH_NAME'" }, { "name":"'commit'", "value":"'$GITHUB_SHA'" } ]' \
78+
echo $'[ { "name":"'branch'", "value":"'$BRANCH_NAME'" },' \
79+
' { "name":"'commit'", "value":"'$GITHUB_SHA'" },' \
80+
' { "name":"'location'", "value":"UK South" },' \
81+
' { "name":"'devopsServicePrincipalId'", "value":"'$SERVICEPRINCIPALID'" }' \
82+
']' \
6983
| jq '.' > "$PARAMETERS_FILE"
7084
cat $PARAMETERS_FILE
7185
@@ -80,6 +94,7 @@ jobs:
8094
--artifact-source-name $ARTIFACT_SOURCE_NAME \
8195
--arm-template $ARM_TEMPLATE_NAME \
8296
--parameter "@$PARAMETERS_FILE" \
97+
--verbose \
8398
| jq '.'
8499
)
85100

Azure-DevTestLab/Environments/sqlcollaborative_AzureDataPipelineTools/azuredeploy.json

Lines changed: 210 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,34 +18,144 @@
1818
"defaultValue": "N/A"
1919
},
2020
"location": {
21+
"type": "string"
22+
},
23+
"deploymentTimestamp": {
2124
"type": "string",
22-
"defaultValue": "UK South"
25+
"defaultValue": "[utcNow()]"
26+
},
27+
"devopsServicePrincipalId": {
28+
"type": "string"
29+
},
30+
"adlsStorageAccountContainerName": {
31+
"type": "string",
32+
"defaultValue": "test"
2333
}
2434
},
2535
"variables": {
26-
"storageAccountApiVersion": "2019-06-01",
27-
"storageAccountName": "[concat('adls', substring(uniqueString(parameters('branch')), 0, 4), 'xxxx', substring(parameters('commit'), 0, min(length(parameters('commit')), 7)))]",
28-
"storageAccountResourceId": "[resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))]"
36+
"storageAccountApiVersion": "2021-04-01",
37+
"adlsStorageAccountName": "[concat('adls', substring(uniqueString(parameters('branch')), 0, 4), 'xxxx', substring(parameters('commit'), 0, min(length(parameters('commit')), 7)))]",
38+
"adlsStorageAccountResourceId": "[resourceId('Microsoft.Storage/storageAccounts', variables('adlsStorageAccountName'))]",
39+
//"adlsStorageAccountContainerName": "test",
40+
41+
"functionsAppApiVersion": "2015-08-01",
42+
"functionsAppBlobStorageAccountName": "[concat('funcblob', substring(uniqueString(parameters('branch')), 0, 4), 'xxxx', substring(parameters('commit'), 0, min(length(parameters('commit')), 7)))]",
43+
"functionsAppName": "[concat('func', substring(uniqueString(parameters('branch')), 0, 4), 'xxxx', substring(parameters('commit'), 0, min(length(parameters('commit')), 7)))]",
44+
45+
"authorizationApiVersion": "2018-09-01-preview",
46+
47+
"owner": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
48+
"contributor": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
49+
"reader": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
50+
51+
"storageBlobDataContributor": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]",
52+
"storageBlobDatareader": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')]"
2953
},
3054
"resources": [
55+
/********************************************************************************************************************************************
56+
**** Resource group permissions
57+
********************************************************************************************************************************************/
58+
59+
// Add the devops service principal as a contributor on the resource group (DevTest Labs is configured to create an RG for
60+
// each lab).
61+
62+
// 'Reader' scoped to the resource group
63+
{
64+
"type": "Microsoft.Authorization/roleAssignments",
65+
"apiVersion": "[variables('authorizationApiVersion')]",
66+
"name": "[guid(resourceGroup().id, 'devopsServicePrincipal_rg_contributor')]",
67+
"properties": {
68+
"roleDefinitionId": "[variables('contributor')]",
69+
"principalId": "[parameters('devopsServicePrincipalId')]"
70+
}
71+
},
72+
73+
// 'Storage Blob Data Reader' scoped to the storage account
74+
{
75+
"type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
76+
"name": "[concat(variables('adlsStorageAccountName'),'/Microsoft.Authorization/',guid(resourceGroup().id, 'devopsServicePrincipal_adlsStorageAccount_storageBlobDataReader'))]",
77+
"apiVersion": "[variables('authorizationApiVersion')]",
78+
"properties": {
79+
"roleDefinitionId": "[variables('storageBlobDatareader')]",
80+
"principalId": "[parameters('devopsServicePrincipalId')]"
81+
},
82+
"dependsOn": [
83+
"[concat('Microsoft.Storage/storageAccounts/', variables('adlsStorageAccountName'))]"
84+
]
85+
},
86+
87+
// 'Storage Blob Data Contributor' scoped to the storage account container
88+
{
89+
"type": "Microsoft.Storage/storageAccounts/blobServices/containers/providers/roleAssignments",
90+
//"name": "[concat(variables('adlsStorageAccountName'), '/default/', parameters('adlsStorageAccountContainerName'), '/Microsoft.Authorization/', guid(resourceGroup().id, 'devopsServicePrincipal_adlsStorageAccountContainer', parameters('adlsStorageAccountContainerName'), 'test_storageBlobDataContributor'))]",
91+
"name": "[concat(variables('adlsStorageAccountName'), '/default/', parameters('adlsStorageAccountContainerName'), '/Microsoft.Authorization/', guid(resourceGroup().id, parameters('devopsServicePrincipalId'), variables('adlsStorageAccountName'), parameters('adlsStorageAccountContainerName'), variables('storageBlobDataContributor')))]",
92+
"apiVersion": "[variables('authorizationApiVersion')]",
93+
//"scope": "[concat(resourceGroup().id, '/providers/Microsoft.Storage/storageAccounts/', variables('adlsStorageAccountName'), '/blobServices/containers/containers/', variables('adlsStorageAccountContainerName'))]",
94+
"properties": {
95+
"roleDefinitionId": "[variables('storageBlobDataContributor')]",
96+
"principalId": "[parameters('devopsServicePrincipalId')]"
97+
},
98+
"dependsOn": [
99+
"[concat('Microsoft.Storage/storageAccounts/', variables('adlsStorageAccountName'), '/blobServices/default/containers/', parameters('adlsStorageAccountContainerName'))]"
100+
]
101+
},
102+
103+
// Add IAM access for functions app. See the following page for details of how to get the object id for the SPN
104+
// https://www.codeisahighway.com/there-is-a-new-way-to-reference-managed-identity-in-arm-template/
105+
106+
// 'Reader' scoped to the storage account
107+
{
108+
"type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
109+
"name": "[concat(variables('adlsStorageAccountName'),'/Microsoft.Authorization/',guid(resourceGroup().id, variables('functionsAppName'), variables('adlsStorageAccountName'), parameters('adlsStorageAccountContainerName'), variables('reader')))]",
110+
"apiVersion": "[variables('authorizationApiVersion')]",
111+
"properties": {
112+
"roleDefinitionId": "[variables('reader')]",
113+
"principalId": "[reference(resourceId('Microsoft.Web/sites', variables('functionsAppName')), variables('functionsAppApiVersion'), 'full').identity.principalId]"
114+
},
115+
"dependsOn": [
116+
"[concat('Microsoft.Storage/storageAccounts/', variables('adlsStorageAccountName'))]",
117+
"[resourceId('Microsoft.Web/sites', variables('functionsAppName'))]"
118+
]
119+
},
120+
121+
// 'Storage Blob Data Reader' scoped to the storage account
122+
{
123+
"type": "Microsoft.Storage/storageAccounts/blobServices/containers/providers/roleAssignments",
124+
"name": "[concat(variables('adlsStorageAccountName'), '/default/', parameters('adlsStorageAccountContainerName'), '/Microsoft.Authorization/', guid(resourceGroup().id, variables('functionsAppName'), variables('adlsStorageAccountName'), parameters('adlsStorageAccountContainerName'), variables('storageBlobDatareader')))]",
125+
"apiVersion": "[variables('authorizationApiVersion')]",
126+
"properties": {
127+
"roleDefinitionId": "[variables('storageBlobDatareader')]",
128+
"principalId": "[reference(resourceId('Microsoft.Web/sites', variables('functionsAppName')), variables('functionsAppApiVersion'), 'full').identity.principalId]"
129+
},
130+
"dependsOn": [
131+
"[concat('Microsoft.Storage/storageAccounts/', variables('adlsStorageAccountName'))]",
132+
"[resourceId('Microsoft.Web/sites', variables('functionsAppName'))]"
133+
]
134+
},
135+
136+
/********************************************************************************************************************************************
137+
**** ADLS storage
138+
********************************************************************************************************************************************/
31139
{
32-
"name": "[variables('storageAccountName')]",
140+
"name": "[variables('adlsStorageAccountName')]",
33141
"type": "Microsoft.Storage/storageAccounts",
34142
"apiVersion": "[variables('storageAccountApiVersion')]",
35143
"location": "[parameters('location')]",
36144
"properties": {
37145
"accessTier": "Hot",
38146
"minimumTlsVersion": "TLS1_2",
39147
"supportsHttpsTrafficOnly": true,
40-
"allowBlobPublicAccess": true,
148+
"allowBlobPublicAccess": false,
41149
"allowSharedKeyAccess": true,
42150
"isHnsEnabled": true,
43151
"networkAcls": {
44152
"bypass": "AzureServices",
45153
"defaultAction": "Allow"
46154
}
47155
},
48-
"dependsOn": [],
156+
"dependsOn": [
157+
"[resourceId('Microsoft.Web/sites', variables('functionsAppName'))]"
158+
],
49159
"sku": {
50160
"name": "Standard_LRS"
51161
},
@@ -55,18 +165,108 @@
55165
"Commit": "[parameters('commit')]",
56166
"Branch": "[parameters('branch')]",
57167
"Pull Request": "[parameters('pullRequest')]",
58-
"Create Date Time": "2021-04-28T12:08:00"
168+
"Create Date Time": "[parameters('deploymentTimestamp')]",
169+
"Git Project Resource Code": "ADLS",
170+
"RG": "[resourceGroup().name]"
171+
},
172+
"resources": [
173+
// Add a container to the storage account
174+
{
175+
"name": "[concat('default/', parameters('adlsStorageAccountContainerName'))]",
176+
"type": "blobServices/containers",
177+
"apiVersion": "[variables('storageAccountApiVersion')]",
178+
"properties": {
179+
"publicAccess": "None"
180+
},
181+
"dependsOn": [
182+
"[variables('adlsStorageAccountName')]"
183+
]
184+
}
185+
]
186+
},
187+
188+
189+
/********************************************************************************************************************************************
190+
**** Functions Apps
191+
********************************************************************************************************************************************/
192+
193+
// Blob storage for the functions app
194+
{
195+
"apiVersion": "[variables('storageAccountApiVersion')]",
196+
"type": "Microsoft.Storage/storageAccounts",
197+
"name": "[variables('functionsAppBlobStorageAccountName')]",
198+
"location": "[parameters('location')]",
199+
"tags": {
200+
"Git Project": "[parameters('gitProject')]",
201+
"Commit": "[parameters('commit')]",
202+
"Branch": "[parameters('branch')]",
203+
"Pull Request": "[parameters('pullRequest')]",
204+
"Create Date Time": "[parameters('deploymentTimestamp')]",
205+
"Git Project Resource Code": "FunctionsAppStorage"
206+
},
207+
"sku": {
208+
"name": "Standard_LRS"
209+
},
210+
"properties": {
211+
"supportsHttpsTrafficOnly": true,
212+
"minimumTlsVersion": "TLS1_2"
213+
}
214+
},
215+
216+
// Functions app, cofigured to use .Net Core 3.X with the blob storage above
217+
{
218+
"apiVersion": "[variables('functionsAppApiVersion')]",
219+
"type": "Microsoft.Web/sites",
220+
"name": "[variables('functionsAppName')]",
221+
"location": "[parameters('location')]",
222+
"kind": "functionapp",
223+
"identity": {
224+
"type": "SystemAssigned"
225+
},
226+
"dependsOn": [
227+
"[resourceId('Microsoft.Storage/storageAccounts', variables('functionsAppBlobStorageAccountName'))]"
228+
],
229+
"tags": {
230+
"Git Project": "[parameters('gitProject')]",
231+
"Commit": "[parameters('commit')]",
232+
"Branch": "[parameters('branch')]",
233+
"Pull Request": "[parameters('pullRequest')]",
234+
"Create Date Time": "[parameters('deploymentTimestamp')]",
235+
"Git Project Resource Code": "FunctionsApp"
236+
},
237+
"properties": {
238+
"siteConfig": {
239+
"appSettings": [
240+
{
241+
"name": "FUNCTIONS_EXTENSION_VERSION",
242+
"value": "~3"
243+
},
244+
{
245+
"name": "FUNCTIONS_WORKER_RUNTIME",
246+
"value": "dotnet"
247+
},
248+
{
249+
"name": "AzureWebJobsStorage",
250+
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('functionsAppBlobStorageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('functionsAppBlobStorageAccountName')), variables('storageAccountApiVersion')).keys[0].value,';EndpointSuffix=','core.windows.net')]"
251+
}
252+
]
253+
}
59254
}
60255
}
61256
],
257+
258+
/********************************************************************************************************************************************
259+
**** Outputs, does not seem to help when deploying uzing the Azure CLI 'az lab environment create' command
260+
********************************************************************************************************************************************/
261+
62262
"outputs": {
63263
"storageAccountName": {
64264
"type": "string",
65-
"value": "[variables('storageAccountName')]"
265+
"value": "[variables('adlsStorageAccountName')]"
66266
},
67267
"storageAccountConnectionString": {
68268
"type": "string",
69-
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storageAccountName'), ';AccountKey=', listKeys(variables('storageAccountResourceId'), variables('storageAccountApiVersion')).keys[0].value)]"
269+
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('adlsStorageAccountName'), ';AccountKey=', listKeys(variables('adlsStorageAccountResourceId'), variables('storageAccountApiVersion')).keys[0].value)]"
70270
}
71271
}
72272
}

0 commit comments

Comments
 (0)