Skip to content
This repository was archived by the owner on Aug 5, 2024. It is now read-only.

Commit 05248eb

Browse files
committed
more provisioning work
1 parent 5774554 commit 05248eb

File tree

4 files changed

+623
-6
lines changed

4 files changed

+623
-6
lines changed

azuredeploy.json

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
33
"contentVersion": "1.0.0.0",
44
"parameters": {
5+
"gatewayVersion": {
6+
"type": "string",
7+
"defaultValue": "",
8+
"metadata": {
9+
"description": "Version of the DeviceScript Gateway package"
10+
}
11+
},
512
"namePrefix": {
613
"type": "string",
714
"minLength": 3,
@@ -207,7 +214,8 @@
207214
"name": "[variables('webAppName')]",
208215
"location": "[variables('location')]",
209216
"tags": {
210-
"displayName": "[variables('webAppName')]"
217+
"displayName": "[variables('webAppName')]",
218+
"deviceScriptGateway": "[parameters('gatewayVersion')]"
211219
},
212220
"dependsOn": [
213221
"[variables('serverFarmId')]",
@@ -247,11 +255,11 @@
247255
}
248256
],
249257
"outputs": {
250-
"admin-token": {
251-
"type": "securestring",
252-
"value": "[variables('adminToken')]"
258+
"keyVaultName": {
259+
"type": "string",
260+
"value": "[variables('keyVaultName')]"
253261
},
254-
"web-app-name": {
262+
"webAppName": {
255263
"type": "string",
256264
"value": "[variables('webAppName')]"
257265
}

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"description": "",
55
"license": "MIT",
66
"scripts": {
7+
"provision": "zx ./provision.mjs",
78
"build": "tsc",
89
"watch": "tsc -w",
910
"start": "node dist/index.js",
@@ -30,9 +31,12 @@
3031
"swagger-ui-dist": "^4.15.0"
3132
},
3233
"devDependencies": {
34+
"@octokit/request": "^6.2.3",
3335
"@types/http-errors": "^1.8.2",
3436
"@types/node": "16.x",
3537
"@types/ws": "^8.5.3",
36-
"typescript": "^4.0.0"
38+
"typescript": "^4.0.0",
39+
"xmlbuilder2": "^3.0.2",
40+
"zx": "^7.2.0"
3741
}
3842
}

provision.mjs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#!/usr/bin/env zx
2+
3+
import "zx/globals"
4+
import { randomBytes } from "crypto"
5+
import { request } from "@octokit/request"
6+
import { readFileSync } from "fs"
7+
import { create } from "xmlbuilder2"
8+
9+
echo(`DeviceScript Gateway configuration.`)
10+
echo(``)
11+
echo(`This script will create a new resource group, with a web app, application insights, key vault and storage account.`)
12+
echo(`Make sure that you have the Azure CLI available and you are logged in.`)
13+
echo(``)
14+
15+
const gatewayVersion = JSON.parse(readFileSync("./package.json", { encoding: "utf8" })).version
16+
echo(chalk.blue(`gateway version: ${gatewayVersion}`))
17+
18+
const resourceGroup = await question(chalk.blue("Pick a name for the resource group: "))
19+
if (!resourceGroup) throw "no resource group name given"
20+
21+
// check if resource group already exists
22+
echo(`Searching for existing resource group ${resourceGroup}...`)
23+
const exists = JSON.parse((await $`az group list --query "[?name=='${resourceGroup}']"`).stdout)
24+
if (exists?.length) {
25+
const config = await question(chalk.red("Resource group already exists, delete? (yes/no) "), { choices: ["yes", "no"] })
26+
if (config !== "yes") throw "resource group already exists"
27+
28+
echo(`deleting resource group ${resourceGroup}...`)
29+
await $`resourceGroup="${resourceGroup}"
30+
az group delete --yes --name $resourceGroup`
31+
}
32+
33+
const namePrefix = await question(chalk.blue("Pick a name prefix for generated resources (unique, > 3 and < 13 characters): "))
34+
if (!namePrefix) throw "no name prefix given"
35+
36+
// check keyvaults already exist
37+
echo(`Looking for deleting keyvaults that might name clash...`)
38+
const deletevaults = JSON.parse((await $`az keyvault list-deleted`).stdout)
39+
const deletedvault = deletevaults?.find(v => v.name === `${namePrefix.toLowerCase()}keys`)
40+
if (deletedvault)
41+
throw `delete keyvault ${deletedvault.name} already exists`
42+
43+
// fetch current user azure id
44+
echo(`Resolving Azure sign in user information...`)
45+
const userInfo = JSON.parse((await $`az ad signed-in-user show`).stdout)
46+
const adminUserId = userInfo.id
47+
echo(chalk.blue(`Azure signin user: ${userInfo.displayName}, ${adminUserId}`))
48+
49+
// generate password
50+
const adminPassword = randomBytes(64).toString('base64url')
51+
52+
// write parameter file
53+
const parameterFile = `azuredeploy.parameters.json`
54+
echo`write ${parameterFile}`
55+
fs.writeFileSync(parameterFile, JSON.stringify({
56+
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
57+
"contentVersion": "1.0.0.0",
58+
"parameters": {
59+
"gatewayVersion": {
60+
"value": gatewayVersion
61+
},
62+
"namePrefix": {
63+
"value": namePrefix
64+
},
65+
"adminUserId": {
66+
"value": adminUserId
67+
},
68+
"adminPassword": {
69+
"value": adminPassword
70+
}
71+
}
72+
}, null, 4), { encoding: "utf8" })
73+
74+
const rsinfo = JSON.parse((await $`resourceGroup="${resourceGroup}"
75+
az group create --name $resourceGroup --location centralus`).stdout)
76+
77+
echo(chalk.blue(`Resource group: ${rsinfo.name}, ${rsinfo.id}`))
78+
79+
// create resources
80+
const dinfo = JSON.parse((await $`resourceGroup="${resourceGroup}"
81+
templateFile="azuredeploy.json"
82+
parametersFile="${parameterFile}"
83+
az deployment group create \
84+
--name devicescript \
85+
--resource-group $resourceGroup \
86+
--template-file $templateFile \
87+
--parameters $parametersFile`).stdout)
88+
const did = dinfo
89+
const { outputs } = dinfo.properties
90+
const { webAppName, keyVaultName } = outputs
91+
92+
echo(chalk.blue(`Deployment: web app ${webAppName}, vault ${keyVaultName}`))
93+
94+
// generate local resource file
95+
fs.writeFileSync(".env",
96+
`RESOURCE_GROUP="${resourceGroup}"
97+
KEY_VAULT_NAME="${keyVaultName}"
98+
SELF_URL="http://0.0.0.0:7071"`, { encoding: "utf8" })
99+
100+
// download publish profile
101+
const pb = JSON.parse((await $`resourceGroup="${resourceGroup}"
102+
name="${webAppName}"
103+
az webapp deployment list-publishing-profiles --name $name --resource-group $resourceGroup`).stdout)
104+
const zpb = pb?.filter(o => o.publishMethod === "ZipDeploy")
105+
if (!zpb) throw "failed to fetch zip deploy publishing profile"
106+
107+
echo('download publish profile...')
108+
const doc = create()
109+
const pp = doc.ele('publishData').ele('publishProfile')
110+
Object.keys(zpd).forEach(key => pp.att(key, zpd[key]))
111+
const pfn = `${webAppName}.PublishSettings`
112+
const xzpb = doc.end({ prettyPrint: true })
113+
echo(`publish profile: ${pfn}`)
114+
fs.writeFileSync(pfn, xzpb, { encoding: "utf8" })
115+
116+
// final notes
117+
echo(chalk.blue(`Azure resources and local development configured successfully`))
118+
echo(`- add GitHub secret AZURE_WEBAPP_PUBLISH_PROFILE with the content of ${pfn}`)
119+
echo(`- navigate to https://${webAppName}.azurewebsites.net/swagger/`)
120+
echo(` and sign in as user: admin, password: ${adminPassword}`)
121+
echo(` (you can find the key in vault ${keyVaultName}/secrets/passwords.)`)

0 commit comments

Comments
 (0)