This commit is contained in:
Noel Bundick 2020-05-04 01:21:05 -07:00
Родитель 61bad49363
Коммит 819ce5cf83
23 изменённых файлов: 2775 добавлений и 182 удалений

Просмотреть файл

@ -1,7 +1,9 @@
.env
.git
.github
.vscode
build
dist
docs
*Dockerfile*
node_modules

5
.ngrok.yml Normal file
Просмотреть файл

@ -0,0 +1,5 @@
tunnels:
deploy:
addr: 8080
proto: http
bind_tls: true

2
.vscode/extensions.json поставляемый
Просмотреть файл

@ -2,6 +2,8 @@
"recommendations": [
"hbenl.vscode-mocha-test-adapter",
"humao.rest-client",
"ms-azuretools.vscode-azureappservice",
"msazurermtools.azurerm-vscode-tools",
"visualstudioexptteam.vscodeintellicode"
]
}

Просмотреть файл

@ -18,7 +18,7 @@ RUN chown node:node .
USER node
COPY --from=build /app/package*.json ./
RUN npm install --ignore-scripts
RUN npm install
COPY --from=build /app/build/src /app/build/src

Просмотреть файл

@ -217,9 +217,22 @@ f4156ae0-6bad-11ea-bd94-8fa64eaf2878 unknown 2020-03-21 12:55:45 PDT
~~~
## Deploying SDS to the cloud
TODO: instructions for Azure deployment and client configuration.
Requirements
* [azure-cli](https://aka.ms/azure-cli)
* [jq](https://stedolan.github.io/jq/)
* [Packer](https://www.packer.io/)
Run the following commands in a bash terminal
~~~
# Create a resource group to hold all the sandbox resources
az group create -n sandbox -l southcentralus
# Deploy an instance of the sandbox
./deploy/deploy.sh -g sandbox
~~~
## Contributing

Просмотреть файл

@ -0,0 +1,73 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"assetsBaseUrl": {
"type": "string",
"defaultValue": ""
},
"baseName": {
"type": "string",
"defaultValue": "dct"
},
"sshPublicKey": {
"type": "securestring",
"defaultValue": ""
}
},
"variables": {
"assetsBaseUrl": "[if(empty(parameters('assetsBaseUrl')), if(contains(deployment().properties, 'templateLink'), uri(deployment().properties.templateLink.uri, '../'), 'https://raw.githubusercontent.com/microsoft/data-contest-toolkit/master/deploy/'), parameters('assetsBaseUrl'))]",
"environmentDeployment": "azuredeploy"
},
"resources": [
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2017-05-10",
"name": "[variables('environmentDeployment')]",
"resourceGroup": "[resourceGroup().name]",
"properties": {
"mode": "Incremental",
"parameters": {
"assetsBaseUrl": {
"value": "[parameters('assetsBaseUrl')]"
},
"baseName": {
"value": "[parameters('baseName')]"
},
"workerVmSize": {
"value": "Standard_B2s"
}
},
"templateLink": {
"uri": "[uri(variables('assetsBaseUrl'), './azure/azuredeploy.json')]"
}
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Resources/deployments', variables('environmentDeployment'))]"
],
"type": "Microsoft.Resources/deployments",
"apiVersion": "2017-05-10",
"name": "dev",
"resourceGroup": "[resourceGroup().name]",
"properties": {
"mode": "Incremental",
"parameters": {
"sshPublicKey": {
"value": "[parameters('sshPublicKey')]"
}
},
"templateLink": {
"uri": "[uri(variables('assetsBaseUrl'), './azure/dev.json')]"
}
}
}
],
"outputs": {
"laboratorySiteId": {
"type": "string",
"value": "[reference(variables('environmentDeployment')).outputs.laboratorySiteId.value]"
}
}
}

Просмотреть файл

@ -0,0 +1,217 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"assetsBaseUrl": {
"type": "string",
"defaultValue": ""
},
"baseName": {
"type": "string",
"defaultValue": "dct"
},
"workerVmSize": {
"type": "string",
"defaultValue": "Standard_DS2_v2"
}
},
"variables": {
"assetsBaseUrl": "[if(empty(parameters('assetsBaseUrl')), if(contains(deployment().properties, 'templateLink'), uri(deployment().properties.templateLink.uri, '../'), 'https://raw.githubusercontent.com/microsoft/data-contest-toolkit/master/deploy/'), parameters('assetsBaseUrl'))]",
"bootstrapIdentity": "bootstrap",
"bootstrapDeployment": "bootstrap",
"laboratoryDeployment": "laboratory",
"monitoringDeployment": "monitoring",
"networkDeployment": "network",
"privatelinkDeployment": "privatelink",
"workerDeployment": "worker",
"workerIdentity": "worker"
},
"resources": [
{
"type": "Microsoft.ManagedIdentity/userAssignedIdentities",
"apiVersion": "2018-11-30",
"location": "[resourceGroup().location]",
"name": "[variables('bootstrapIdentity')]"
},
{
"type": "Microsoft.ManagedIdentity/userAssignedIdentities",
"apiVersion": "2018-11-30",
"location": "[resourceGroup().location]",
"name": "[variables('workerIdentity')]"
},
{
"type": "Microsoft.Resources/deployments",
"apiVersion": "2017-05-10",
"name": "[variables('networkDeployment')]",
"resourceGroup": "[resourceGroup().name]",
"properties": {
"mode": "Incremental",
"templateLink": {
"uri": "[uri(variables('assetsBaseUrl'), './azure/network.json')]"
}
}
},
{
"dependsOn": [
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('bootstrapIdentity'))]",
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('workerIdentity'))]",
"[resourceId('Microsoft.Resources/deployments', variables('networkDeployment'))]"
],
"type": "Microsoft.Resources/deployments",
"apiVersion": "2017-05-10",
"name": "[variables('laboratoryDeployment')]",
"resourceGroup": "[resourceGroup().name]",
"properties": {
"mode": "Incremental",
"parameters": {
"baseName": {
"value": "[parameters('baseName')]"
},
"bootstrapIdentityId": {
"value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('bootstrapIdentity'))]"
},
"privateDnsId": {
"value": "[reference(variables('networkDeployment')).outputs.privateDnsId.value]"
},
"subnetId": {
"value": "[reference(variables('networkDeployment')).outputs.laboratorySubnetId.value]"
},
"workerIdentityId": {
"value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('workerIdentity'))]"
}
},
"templateLink": {
"uri": "[uri(variables('assetsBaseUrl'), './azure/laboratory.json')]"
}
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Resources/deployments', variables('laboratoryDeployment'))]",
"[resourceId('Microsoft.Resources/deployments', variables('networkDeployment'))]"
],
"type": "Microsoft.Resources/deployments",
"apiVersion": "2017-05-10",
"name": "[variables('privatelinkDeployment')]",
"resourceGroup": "[resourceGroup().name]",
"properties": {
"mode": "Incremental",
"parameters": {
"laboratoryRegistryId": {
"value": "[reference(variables('laboratoryDeployment')).outputs.registryId.value]"
},
"laboratorySqlServerId": {
"value": "[reference(variables('laboratoryDeployment')).outputs.sqlServerId.value]"
},
"laboratoryStorageAccountId": {
"value": "[reference(variables('laboratoryDeployment')).outputs.storageAccountId.value]"
},
"subnetId": {
"value": "[reference(variables('networkDeployment')).outputs.paasSubnetId.value]"
},
"vnetId": {
"value": "[reference(variables('networkDeployment')).outputs.vnetId.value]"
}
},
"templateLink": {
"uri": "[uri(variables('assetsBaseUrl'), './azure/privatelink.json')]"
}
}
},
{
"dependsOn": [
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('workerIdentity'))]",
"[resourceId('Microsoft.Resources/deployments', variables('bootstrapDeployment'))]",
"[resourceId('Microsoft.Resources/deployments', variables('laboratoryDeployment'))]",
"[resourceId('Microsoft.Resources/deployments', variables('networkDeployment'))]"
],
"type": "Microsoft.Resources/deployments",
"apiVersion": "2017-05-10",
"name": "[variables('workerDeployment')]",
"resourceGroup": "[resourceGroup().name]",
"properties": {
"mode": "Incremental",
"parameters": {
"asgId": {
"value": "[reference(variables('networkDeployment')).outputs.workerAsgId.value]"
},
"identityId": {
"value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('workerIdentity'))]"
},
"subnetId": {
"value": "[reference(variables('networkDeployment')).outputs.lockdownSubnetId.value]"
},
"vmSize": {
"value": "[parameters('workerVmSize')]"
}
},
"templateLink": {
"uri": "[uri(variables('assetsBaseUrl'), './azure/worker.vm.json')]"
}
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Resources/deployments', variables('laboratoryDeployment'))]"
],
"type": "Microsoft.Resources/deployments",
"apiVersion": "2017-05-10",
"name": "[variables('monitoringDeployment')]",
"resourceGroup": "[resourceGroup().name]",
"properties": {
"mode": "Incremental",
"parameters": {
"baseName": {
"value": "[parameters('baseName')]"
},
"laboratorySiteId": {
"value": "[reference(variables('laboratoryDeployment')).outputs.siteId.value]"
}
},
"templateLink": {
"uri": "[uri(variables('assetsBaseUrl'), './azure/monitoring.json')]"
}
}
},
{
"dependsOn": [
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('bootstrapIdentity'))]",
"[resourceId('Microsoft.Resources/deployments', variables('laboratoryDeployment'))]",
"[resourceId('Microsoft.Resources/deployments', variables('monitoringDeployment'))]",
"[resourceId('Microsoft.Resources/deployments', variables('networkDeployment'))]",
"[resourceId('Microsoft.Resources/deployments', variables('privatelinkDeployment'))]"
],
"type": "Microsoft.Resources/deployments",
"apiVersion": "2017-05-10",
"name": "[variables('bootstrapDeployment')]",
"resourceGroup": "[resourceGroup().name]",
"properties": {
"mode": "Incremental",
"parameters": {
"asgId": {
"value": "[reference(variables('networkDeployment')).outputs.bootstrapAsgId.value]"
},
"identityId": {
"value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('bootstrapIdentity'))]"
},
"subnetId": {
"value": "[reference(variables('networkDeployment')).outputs.bootstrapSubnetId.value]"
}
},
"templateLink": {
"uri": "[uri(variables('assetsBaseUrl'), './azure/bootstrap.json')]"
}
}
}
],
"outputs": {
"laboratoryRegistryName": {
"type": "string",
"value": "[last(split(reference(variables('laboratoryDeployment')).outputs.registryId.value, '/'))]"
},
"laboratorySiteId": {
"type": "string",
"value": "[reference(variables('laboratoryDeployment')).outputs.siteId.value]"
}
}
}

149
deploy/azure/bootstrap.json Normal file
Просмотреть файл

@ -0,0 +1,149 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"assetsBaseUrl": {
"type": "string",
"defaultValue": ""
},
"asgId": {
"type": "string"
},
"identityId": {
"type": "string"
},
"subnetId": {
"type": "string"
},
"vmPassword": {
"type": "securestring",
"defaultValue": "[newGuid()]"
},
"vmSize": {
"type": "string",
"defaultValue": "Standard_B1s"
}
},
"variables": {
"assetsBaseUrl": "[if(empty(parameters('assetsBaseUrl')), if(contains(deployment().properties, 'templateLink'), uri(deployment().properties.templateLink.uri, '../'), 'https://raw.githubusercontent.com/microsoft/data-contest-toolkit/master/deploy/'), parameters('assetsBaseUrl'))]",
"cloudInit": [
"[uri(variables('assetsBaseUrl'), './bootstrap/setup.sh')]",
"[uri(variables('assetsBaseUrl'), './bootstrap/bootstrap.ps1')]"
],
"customData": "[base64(format('#include\n{0}', util.join(variables('cloudInit'), '\n')))]",
"name": "bootstrap",
"virtualMachineContributorRoleId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '9980e02c-c2be-4d73-94e8-173b1dc7cf3c')]"
},
"resources": [
{
"type": "Microsoft.Network/networkInterfaces",
"apiVersion": "2019-11-01",
"location": "[resourceGroup().location]",
"name": "[variables('name')]",
"properties": {
"ipConfigurations": [
{
"name": "default",
"properties": {
"applicationSecurityGroups": [
{
"id": "[parameters('asgId')]"
}
],
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[parameters('subnetId')]"
}
}
}
]
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Network/networkInterfaces', variables('name'))]"
],
"type": "Microsoft.Compute/virtualMachines",
"apiVersion": "2019-07-01",
"location": "[resourceGroup().location]",
"name": "[variables('name')]",
"identity": {
"type": "UserAssigned",
"userAssignedIdentities": {
"[parameters('identityId')]": {
}
}
},
"properties": {
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('name'))]"
}
]
},
"osProfile": {
"adminUsername": "azureuser",
"adminPassword": "[parameters('vmPassword')]",
"computerName": "[variables('name')]",
"customData": "[variables('customData')]"
},
"storageProfile": {
"imageReference": {
"publisher": "Canonical",
"offer": "UbuntuServer",
"sku": "18.04-LTS",
"version": "latest"
},
"osDisk": {
"createOption": "FromImage",
"managedDisk": {
"storageAccountType": "Standard_LRS"
}
}
}
},
"resources": [
{
"dependsOn": [
"[resourceId('Microsoft.Compute/virtualMachines', variables('name'))]"
],
"type": "providers/roleAssignments",
"apiVersion": "2018-09-01-preview",
"name": "[concat('Microsoft.Authorization/', guid(concat(variables('name'), parameters('identityId'), 'VirtualMachineContributor')))]",
"properties": {
"roleDefinitionId": "[variables('virtualMachineContributorRoleId')]",
"principalId": "[reference(parameters('identityId'), '2018-11-30').principalId]",
"principalType": "ServicePrincipal"
}
}
]
}
],
"functions": [
{
"namespace": "util",
"members": {
"join": {
"parameters": [
{
"name": "values",
"type": "array"
},
{
"name": "separator",
"type": "string"
}
],
"output": {
"type": "string",
"value": "[replace(replace(substring(string(parameters('values')), 1, sub(length(string(parameters('values'))), 2)), '\"', ''), ',', parameters('separator'))]"
}
}
}
}
]
}

219
deploy/azure/dev.json Normal file
Просмотреть файл

@ -0,0 +1,219 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"sshPublicKey": {
"type": "securestring",
"defaultValue": ""
}
},
"variables": {
"autoShutdownTime": "0200",
"autoShutdownTimeZone": "UTC",
"bootstrapIp": "bootstrap-dev",
"tags": {
"dct-mode": "development"
},
"workerIp": "worker-dev"
},
"resources": [
{
"name": "default",
"type": "Microsoft.Resources/tags",
"apiVersion": "2019-10-01",
"properties": {
"tags": "[union(resourceGroup().tags, variables('tags'))]"
}
},
{
"name": "shutdown-computevm-bootstrap",
"type": "Microsoft.DevTestLab/schedules",
"apiVersion": "2018-09-15",
"location": "[resourceGroup().location]",
"properties": {
"status": "Enabled",
"dailyRecurrence": {
"time": "[variables('autoShutdownTime')]"
},
"targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', 'bootstrap')]",
"taskType": "ComputeVmShutdownTask",
"timeZoneId": "[variables('autoShutdownTimeZone')]"
}
},
{
"name": "shutdown-computevm-worker",
"type": "Microsoft.DevTestLab/schedules",
"apiVersion": "2018-09-15",
"location": "[resourceGroup().location]",
"properties": {
"status": "Enabled",
"dailyRecurrence": {
"time": "[variables('autoShutdownTime')]"
},
"targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', 'worker')]",
"taskType": "ComputeVmShutdownTask",
"timeZoneId": "[variables('autoShutdownTimeZone')]"
}
},
{
"name": "bootstrap/dev-ssh",
"type": "Microsoft.Network/networkSecurityGroups/securityRules",
"apiVersion": "2019-11-01",
"properties": {
"description": "Allow SSH for development",
"priority": 2000,
"direction": "Inbound",
"access": "Allow",
"protocol": "TCP",
"sourceAddressPrefix": "*",
"sourcePortRange": "*",
"destinationApplicationSecurityGroups": [
{
"id": "[resourceId('Microsoft.Network/applicationSecurityGroups', 'bootstrap')]"
}
],
"destinationPortRange": "22"
}
},
{
"name": "lockdown/dev-ssh",
"type": "Microsoft.Network/networkSecurityGroups/securityRules",
"apiVersion": "2019-11-01",
"properties": {
"description": "Allow SSH for development",
"priority": 2000,
"direction": "Inbound",
"access": "Allow",
"protocol": "TCP",
"sourceAddressPrefix": "*",
"sourcePortRange": "*",
"destinationApplicationSecurityGroups": [
{
"id": "[resourceId('Microsoft.Network/applicationSecurityGroups', 'worker')]"
}
],
"destinationPortRange": "22"
}
},
{
"name": "[variables('bootstrapIp')]",
"type": "Microsoft.Network/publicIPAddresses",
"apiVersion": "2019-11-01",
"location": "[resourceGroup().location]",
"properties": {
"publicIPAllocationMethod": "Static"
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Network/publicIPAddresses', variables('bootstrapIp'))]"
],
"type": "Microsoft.Network/networkInterfaces",
"apiVersion": "2019-11-01",
"location": "[resourceGroup().location]",
"name": "bootstrap",
"properties": {
"ipConfigurations": [
{
"name": "default",
"properties": {
"applicationSecurityGroups": [
{
"id": "[resourceId('Microsoft.Network/applicationSecurityGroups', 'bootstrap')]"
}
],
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('bootstrapIp'))]"
},
"subnet": {
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', 'environment', 'bootstrap')]"
}
}
}
]
}
},
{
"name": "[variables('workerIp')]",
"type": "Microsoft.Network/publicIPAddresses",
"apiVersion": "2019-11-01",
"location": "[resourceGroup().location]",
"properties": {
"publicIPAllocationMethod": "Static"
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Network/publicIPAddresses', variables('workerIp'))]"
],
"type": "Microsoft.Network/networkInterfaces",
"apiVersion": "2019-11-01",
"location": "[resourceGroup().location]",
"name": "worker",
"properties": {
"ipConfigurations": [
{
"name": "default",
"properties": {
"applicationSecurityGroups": [
{
"id": "[resourceId('Microsoft.Network/applicationSecurityGroups', 'worker')]"
}
],
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('workerIp'))]"
},
"subnet": {
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', 'environment', 'lockdown')]"
}
}
}
]
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Network/networkInterfaces', 'bootstrap')]"
],
"condition": "[not(empty(parameters('sshPublicKey')))]",
"type": "Microsoft.Compute/virtualMachines/extensions",
"apiVersion": "2019-07-01",
"location": "[resourceGroup().location]",
"name": "bootstrap/vmAccess",
"properties": {
"publisher": "Microsoft.OSTCExtensions",
"type": "VMAccessForLinux",
"typeHandlerVersion": "1.5",
"autoUpgradeMinorVersion": true,
"settings": {},
"protectedSettings": {
"username": "azureuser",
"ssh_key": "[parameters('sshPublicKey')]"
}
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Network/networkInterfaces', 'worker')]"
],
"condition": "[not(empty(parameters('sshPublicKey')))]",
"type": "Microsoft.Compute/virtualMachines/extensions",
"apiVersion": "2019-07-01",
"location": "[resourceGroup().location]",
"name": "worker/vmAccess",
"properties": {
"publisher": "Microsoft.OSTCExtensions",
"type": "VMAccessForLinux",
"typeHandlerVersion": "1.5",
"autoUpgradeMinorVersion": true,
"settings": {},
"protectedSettings": {
"username": "azureuser",
"ssh_key": "[parameters('sshPublicKey')]"
}
}
}
]
}

Просмотреть файл

@ -0,0 +1,412 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"bootstrapIdentityId": {
"type": "string"
},
"privateDnsId": {
"type": "string"
},
"subnetId": {
"type": "string"
},
"workerIdentityId": {
"type": "string"
},
"baseName": {
"type": "string",
"defaultValue": "dct"
},
"sqlAdminUsername": {
"type": "string",
"defaultValue": "[parameters('baseName')]"
},
"sqlAdminPassword": {
"type": "securestring",
"defaultValue": "[newGuid()]"
}
},
"variables": {
"acrPullRoleId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')]",
"appSvcPlan": "laboratory",
"identity": "laboratory",
"metadataStorage": "[concat(variables('prefix'), 'laboratory')]",
"privateDns": "[last(split(parameters('privateDnsId'), '/'))]",
"registry": "[concat(variables('prefix'), 'laboratory')]",
"runsContainer": "runs",
"runsQueue": "runs",
"site": "[concat(variables('prefix'), '-laboratory')]",
"sqlDatabase": "laboratory",
"sqlServer": "[concat(variables('prefix'), '-laboratory')]",
"prefix": "[toLower(concat(parameters('baseName'), take(uniqueString(resourceGroup().id), 6)))]",
"storageBlobDataContributorRoleId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]",
"storageQueueDataContributorRoleId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '974c5e8b-45b9-4653-ba55-5f855dd0fb88')]",
"storageQueueDataMessageProcessorRoleId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8a0f0c08-91a1-4084-bc3d-661d67233fed')]",
"storageQueueDataMessageSenderRoleId": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c6a89b2d-59bc-44d0-9896-0f6e12d7b80a')]"
},
"resources": [
{
"type": "Microsoft.ManagedIdentity/userAssignedIdentities",
"apiVersion": "2018-11-30",
"location": "[resourceGroup().location]",
"name": "[variables('identity')]"
},
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2019-06-01",
"location": "[resourceGroup().location]",
"name": "[variables('metadataStorage')]",
"kind": "StorageV2",
"sku": {
"name": "Standard_LRS"
},
"properties": {
"accessTier": "Hot",
"encryption": {
"keySource": "Microsoft.Storage",
"services": {
"blob": {
"enabled": true
},
"queue": {
"enabled": true
}
}
},
"isHnsEnabled": false,
"networkAcls": {
"bypass": "None",
"defaultAction": "Deny",
"virtualNetworkRules": [
{
"id": "[parameters('subnetId')]",
"action": "Allow"
}
]
},
"supportsHttpsTrafficOnly": true
},
"resources": [
{
"dependsOn": [
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('identity'))]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('metadataStorage'))]"
],
"type": "providers/roleAssignments",
"apiVersion": "2018-09-01-preview",
"name": "[concat('Microsoft.Authorization/', guid(concat(variables('metadataStorage'), variables('identity'), 'StorageQueueDataMessageSender')))]",
"properties": {
"roleDefinitionId": "[variables('storageQueueDataMessageSenderRoleId')]",
"principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('identity'))).principalId]",
"principalType": "ServicePrincipal"
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('metadataStorage'))]"
],
"type": "providers/roleAssignments",
"apiVersion": "2018-09-01-preview",
"name": "[concat('Microsoft.Authorization/', guid(concat(variables('metadataStorage'), parameters('bootstrapIdentityId'), 'StorageQueueDataContributor')))]",
"properties": {
"roleDefinitionId": "[variables('storageQueueDataContributorRoleId')]",
"principalId": "[reference(parameters('bootstrapIdentityId'), '2018-11-30').principalId]",
"principalType": "ServicePrincipal"
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('metadataStorage'))]"
],
"type": "providers/roleAssignments",
"apiVersion": "2018-09-01-preview",
"name": "[concat('Microsoft.Authorization/', guid(concat(variables('metadataStorage'), parameters('workerIdentityId'), 'StorageQueueDataMessageProcessor')))]",
"properties": {
"roleDefinitionId": "[variables('storageQueueDataMessageProcessorRoleId')]",
"principalId": "[reference(parameters('workerIdentityId'), '2018-11-30').principalId]",
"principalType": "ServicePrincipal"
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', variables('metadataStorage'))]"
],
"type": "blobServices/containers",
"apiVersion": "2019-06-01",
"name": "[concat('default/', variables('runsContainer'))]",
"properties": {
"publicAccess": "None"
},
"resources": [
{
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts/blobServices/containers', variables('metadataStorage'), 'default', variables('runsContainer'))]"
],
"type": "providers/roleAssignments",
"apiVersion": "2018-09-01-preview",
"name": "[concat('/Microsoft.Authorization/', guid(variables('metadataStorage'), variables('runsContainer'), parameters('workerIdentityId'), 'StorageBlobDataContributor'))]",
"properties": {
"roleDefinitionId": "[variables('storageBlobDataContributorRoleId')]",
"principalId": "[reference(parameters('workerIdentityId'), '2018-11-30').principalId]",
"principalType": "ServicePrincipal"
}
}
]
}
]
},
{
"type": "Microsoft.Sql/servers",
"apiVersion": "2019-06-01-preview",
"location": "[resourceGroup().location]",
"name": "[variables('sqlServer')]",
"properties": {
"administratorLogin": "[parameters('sqlAdminUsername')]",
"administratorLoginPassword": "[parameters('sqlAdminPassword')]",
"minimalTlsVersion": "1.2",
"version": "12.0"
},
"resources": [
{
"dependsOn": [
"[resourceId('Microsoft.Sql/servers', variables('sqlServer'))]"
],
"type": "administrators",
"apiVersion": "2019-06-01-preview",
"location": "[resourceGroup().location]",
"name": "ActiveDirectory",
"properties": {
"administratorType": "ActiveDirectory",
"login": "[last(split(parameters('bootstrapIdentityId'), '/'))]",
"sid": "[reference(parameters('bootstrapIdentityId'), '2018-11-30').principalId]",
"tenantId": "[subscription().tenantId]"
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Sql/servers', variables('sqlServer'))]"
],
"type": "databases",
"apiVersion": "2019-06-01-preview",
"location": "[resourceGroup().location]",
"name": "[variables('sqlDatabase')]",
"sku": {
"name": "GP_S_Gen5",
"capacity": 1
},
"properties": {
"autoPauseDelay": 60,
"minCapacity": 0.5
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Sql/servers', variables('sqlServer'))]"
],
"type": "virtualNetworkRules",
"apiVersion": "2015-05-01-preview",
"location": "[resourceGroup().location]",
"name": "laboratory",
"properties": {
"virtualNetworkSubnetId": "[parameters('subnetId')]"
}
}
]
},
{
"type": "Microsoft.ContainerRegistry/registries",
"apiVersion": "2019-12-01-preview",
"location": "[resourceGroup().location]",
"name": "[variables('registry')]",
"sku": {
"name": "Premium"
},
"properties": {
"adminUserEnabled": false,
"networkRuleSet": {
"defaultAction": "Deny"
}
},
"resources": [
{
"dependsOn": [
"[resourceId('Microsoft.ContainerRegistry/registries', variables('registry'))]"
],
"type": "providers/roleAssignments",
"apiVersion": "2018-09-01-preview",
"name": "[concat('Microsoft.Authorization/', guid(concat(variables('registry'), parameters('workerIdentityId'), 'AcrPull')))]",
"properties": {
"roleDefinitionId": "[variables('acrPullRoleId')]",
"principalId": "[reference(parameters('workerIdentityId'), '2018-11-30').principalId]",
"principalType": "ServicePrincipal"
}
}
]
},
{
"type": "Microsoft.Web/serverFarms",
"apiVersion": "2019-08-01",
"location": "[resourceGroup().location]",
"name": "[variables('appSvcPlan')]",
"kind": "linux",
"sku": {
"name": "S1"
},
"properties": {
"reserved": true
}
},
{
"dependsOn": [
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('identity'))]",
"[resourceId('Microsoft.Sql/servers', variables('sqlServer'))]",
"[resourceId('Microsoft.Storage/storageAccounts', variables('metadataStorage'))]",
"[resourceId('Microsoft.Web/serverFarms', variables('appSvcPlan'))]"
],
"type": "Microsoft.Web/sites",
"apiVersion": "2019-08-01",
"location": "[resourceGroup().location]",
"name": "[variables('site')]",
"identity": {
"type": "UserAssigned",
"userAssignedIdentities": {
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('identity'))]": {
}
}
},
"properties": {
"httpsOnly": true,
"serverFarmId": "[resourceId('Microsoft.Web/serverFarms', variables('appSvcPlan'))]",
"siteConfig": {
"alwaysOn": true,
"appCommandLine": "npm run laboratory",
"appSettings": [
{
"name": "AZURE_CLIENT_ID",
"value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('identity'))).clientId]"
},
{
"name": "BLOB_CONTAINER",
"value": "[concat(reference(resourceId('Microsoft.Storage/storageAccounts', variables('metadataStorage'))).primaryEndpoints.blob, variables('runsContainer'))]"
},
{
"name": "QUEUE_MODE",
"value": "azure"
},
{
"name": "QUEUE_ENDPOINT",
"value": "[concat(reference(resourceId('Microsoft.Storage/storageAccounts', variables('metadataStorage'))).primaryEndpoints.queue, variables('runsQueue'))]"
},
{
"name": "SQL_MODE",
"value": "azuresql"
},
{
"name": "SQL_HOST",
"value": "[reference(resourceId('Microsoft.Sql/servers', variables('sqlServer'))).fullyQualifiedDomainName]"
},
{
"name": "SQL_DB",
"value": "[variables('sqlDatabase')]"
},
{
"name": "WEBSITE_RUN_FROM_PACKAGE",
"value": "1"
}
],
"linuxFxVersion": "NODE|12-lts",
"minTlsVersion": "1.2"
}
},
"resources": [
{
"dependsOn": [
"[resourceId('Microsoft.Web/sites', variables('site'))]"
],
"type": "networkConfig",
"apiVersion": "2019-08-01",
"location": "[resourceGroup().location]",
"name": "virtualNetwork",
"properties": {
"swiftSupported": true,
"subnetResourceId": "[parameters('subnetId')]"
}
}
]
},
{
"dependsOn": [
"[resourceId('Microsoft.ContainerRegistry/registries', variables('registry'))]",
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('identity'))]",
"[resourceId('Microsoft.Sql/servers', variables('sqlServer'))]"
],
"type": "Microsoft.Network/privateDnsZones/TXT",
"apiVersion": "2018-09-01",
"name": "[concat(variables('privateDns'), '/laboratory')]",
"properties": {
"ttl": 30,
"txtRecords": [
{
"value": [
"[concat('clientId=', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', variables('identity'))).clientId)]"
]
},
{
"value": [
"[concat('identity=', variables('identity'))]"
]
},
{
"value": [
"[concat('runsContainer=', variables('runsContainer'))]"
]
},
{
"value": [
"[concat('runsQueueEndpoint=', concat(reference(resourceId('Microsoft.Storage/storageAccounts', variables('metadataStorage'))).primaryEndpoints.queue, variables('runsQueue')))]"
]
},
{
"value": [
"[concat('sqlDatabase=', variables('sqlDatabase'))]"
]
},
{
"value": [
"[concat('sqlServer=', reference(resourceId('Microsoft.Sql/servers', variables('sqlServer'))).fullyQualifiedDomainName)]"
]
},
{
"value": [
"[concat('storageAccount=', variables('metadataStorage'))]"
]
},
{
"value": [
"[concat('registry=', reference(resourceId('Microsoft.ContainerRegistry/registries', variables('registry'))).loginServer)]"
]
}
]
}
}
],
"outputs": {
"registryId": {
"type": "string",
"value": "[resourceId('Microsoft.ContainerRegistry/registries', variables('registry'))]"
},
"siteId": {
"type": "string",
"value": "[resourceId('Microsoft.Web/sites', variables('site'))]"
},
"sqlServerId": {
"type": "string",
"value": "[resourceId('Microsoft.Sql/servers', variables('sqlServer'))]"
},
"storageAccountId": {
"type": "string",
"value": "[resourceId('Microsoft.Storage/storageAccounts', variables('metadataStorage'))]"
}
}
}

Просмотреть файл

@ -0,0 +1,47 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"baseName": {
"type": "string",
"defaultValue": "dct"
},
"laboratorySiteId": {
"type": "string"
}
},
"variables": {
"appInsights": "[concat(variables('prefix'), '-insights')]",
"prefix": "[toLower(concat(parameters('baseName'), take(uniqueString(resourceGroup().id), 6)))]",
"logAnalytics": "[concat(variables('prefix'), '-logs')]"
},
"resources": [
{
"type": "Microsoft.Insights/components",
"apiVersion": "2015-05-01",
"location": "[resourceGroup().location]",
"name": "[variables('appInsights')]",
"kind": "web",
"tags": {
"[concat('hidden-link:', parameters('laboratorySiteId'))]": "Resource"
},
"properties": {
"Application_Type": "web"
}
},
{
"type": "Microsoft.OperationalInsights/workspaces",
"apiVersion": "2015-11-01-preview",
"location": "[resourceGroup().location]",
"name": "[variables('logAnalytics')]",
"properties": {
"sku": {
"name": "PerGB2018"
},
"features": {
"enableLogAccessUsingOnlyResourcePermissions": true
}
}
}
]
}

330
deploy/azure/network.json Normal file
Просмотреть файл

@ -0,0 +1,330 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
},
"variables": {
"bootstrapAsg": "bootstrap",
"bootstrapSubnet": "bootstrap",
"bootstrapSubnetPrefix": "10.0.0.16/28",
"privateDns": "environment.private",
"paasSubnet": "paas",
"paasSubnetPrefix": "10.0.0.0/28",
"laboratorySubnet": "laboratory",
"laboratorySubnetPrefix": "10.0.0.32/27",
"lockdownSubnet": "lockdown",
"lockdownSubnetPrefix": "10.0.0.64/27",
"vnet": "environment",
"workerAsg": "worker"
},
"resources": [
{
"type": "Microsoft.Network/applicationSecurityGroups",
"apiVersion": "2019-11-01",
"location": "[resourceGroup().location]",
"name": "[variables('workerAsg')]",
"properties": {
}
},
{
"type": "Microsoft.Network/applicationSecurityGroups",
"apiVersion": "2019-11-01",
"location": "[resourceGroup().location]",
"name": "[variables('bootstrapAsg')]",
"properties": {
}
},
{
"type": "Microsoft.Network/networkSecurityGroups",
"apiVersion": "2019-11-01",
"location": "[resourceGroup().location]",
"name": "[variables('bootstrapSubnet')]",
"properties": {
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Network/applicationSecurityGroups', variables('bootstrapAsg'))]",
"[resourceId('Microsoft.Network/applicationSecurityGroups', variables('workerAsg'))]"
],
"type": "Microsoft.Network/networkSecurityGroups",
"apiVersion": "2019-11-01",
"location": "[resourceGroup().location]",
"name": "[variables('paasSubnet')]",
"properties": {
"securityRules": [
{
"name": "all-inbound-deny",
"properties": {
"priority": 4096,
"direction": "Inbound",
"access": "Deny",
"protocol": "*",
"sourceAddressPrefix": "*",
"sourcePortRange": "*",
"destinationAddressPrefix": "*",
"destinationPortRange": "*"
}
},
{
"name": "worker-inbound-allow",
"properties": {
"priority": 1000,
"direction": "Inbound",
"access": "Allow",
"protocol": "TCP",
"sourceApplicationSecurityGroups": [
{
"id": "[resourceId('Microsoft.Network/applicationSecurityGroups', variables('workerAsg'))]"
}
],
"sourcePortRange": "*",
"destinationAddressPrefix": "[variables('paasSubnetPrefix')]",
"destinationPortRange": "443"
}
},
{
"name": "bootstrap-inbound-allow",
"properties": {
"priority": 1200,
"direction": "Inbound",
"access": "Allow",
"protocol": "TCP",
"sourceApplicationSecurityGroups": [
{
"id": "[resourceId('Microsoft.Network/applicationSecurityGroups', variables('bootstrapAsg'))]"
}
],
"sourcePortRange": "*",
"destinationAddressPrefix": "[variables('paasSubnetPrefix')]",
"destinationPortRanges": [
"1433",
"443"
]
}
},
{
"name": "all-outbound-deny",
"properties": {
"priority": 4096,
"direction": "Outbound",
"access": "Deny",
"protocol": "*",
"sourceAddressPrefix": "*",
"sourcePortRange": "*",
"destinationAddressPrefix": "*",
"destinationPortRange": "*"
}
}
]
}
},
{
"type": "Microsoft.Network/networkSecurityGroups",
"apiVersion": "2019-11-01",
"location": "[resourceGroup().location]",
"name": "[variables('laboratorySubnet')]",
"properties": {
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Network/applicationSecurityGroups', variables('workerAsg'))]"
],
"type": "Microsoft.Network/networkSecurityGroups",
"apiVersion": "2019-11-01",
"location": "[resourceGroup().location]",
"name": "[variables('lockdownSubnet')]",
"properties": {
"securityRules": [
{
"name": "all-inbound-deny",
"properties": {
"priority": 4096,
"direction": "Inbound",
"access": "Deny",
"protocol": "*",
"sourceAddressPrefix": "*",
"sourcePortRange": "*",
"destinationAddressPrefix": "*",
"destinationPortRange": "*"
}
},
{
"name": "private-endpoints-outbound-allow",
"properties": {
"priority": 1000,
"direction": "Outbound",
"access": "Allow",
"protocol": "TCP",
"sourceApplicationSecurityGroups": [
{
"id": "[resourceId('Microsoft.Network/applicationSecurityGroups', variables('workerAsg'))]"
}
],
"sourcePortRange": "*",
"destinationAddressPrefix": "[variables('paasSubnetPrefix')]",
"destinationPortRange": "443"
}
},
{
"name": "all-outbound-deny",
"properties": {
"priority": 4096,
"direction": "Outbound",
"access": "Deny",
"protocol": "*",
"sourceAddressPrefix": "*",
"sourcePortRange": "*",
"destinationAddressPrefix": "*",
"destinationPortRange": "*"
}
}
]
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Network/networkSecurityGroups', variables('bootstrapSubnet'))]",
"[resourceId('Microsoft.Network/networkSecurityGroups', variables('paasSubnet'))]",
"[resourceId('Microsoft.Network/networkSecurityGroups', variables('laboratorySubnet'))]",
"[resourceId('Microsoft.Network/networkSecurityGroups', variables('lockdownSubnet'))]"
],
"type": "Microsoft.Network/virtualNetworks",
"apiVersion": "2019-11-01",
"location": "[resourceGroup().location]",
"name": "[variables('vnet')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"10.0.0.0/24"
]
},
"subnets": [
{
"name": "[variables('paasSubnet')]",
"properties": {
"addressPrefix": "[variables('paasSubnetPrefix')]",
"networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('paasSubnet'))]"
},
"privateEndpointNetworkPolicies": "Disabled"
}
},
{
"name": "[variables('bootstrapSubnet')]",
"properties": {
"addressPrefix": "[variables('bootstrapSubnetPrefix')]",
"networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('bootstrapSubnet'))]"
}
}
},
{
"name": "[variables('laboratorySubnet')]",
"properties": {
"addressPrefix": "[variables('laboratorySubnetPrefix')]",
"delegations": [
{
"name": "appService",
"properties": {
"serviceName": "Microsoft.Web/serverfarms"
}
}
],
"networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('laboratorySubnet'))]"
},
"serviceEndpoints": [
{
"service": "Microsoft.Sql",
"locations": [
"[resourceGroup().location]"
]
},
{
"service": "Microsoft.Storage",
"locations": [
"[resourceGroup().location]"
]
}
]
}
},
{
"name": "[variables('lockdownSubnet')]",
"properties": {
"addressPrefix": "[variables('lockdownSubnetPrefix')]",
"networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('lockdownSubnet'))]"
}
}
}
]
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Network/virtualNetworks', variables('vnet'))]"
],
"type": "Microsoft.Network/privateDnsZones",
"apiVersion": "2018-09-01",
"location": "global",
"name": "[variables('privateDns')]",
"properties": {
},
"resources": [
{
"dependsOn": [
"[resourceId('Microsoft.Network/privateDnsZones', variables('privateDns'))]"
],
"type": "virtualNetworkLinks",
"apiVersion": "2018-09-01",
"location": "global",
"name": "privatedns",
"properties": {
"registrationEnabled": false,
"virtualNetwork": {
"id": "[resourceId('Microsoft.Network/virtualNetworks', variables('vnet'))]",
"resourceGroup": "[resourceGroup().name]"
}
}
}
]
}
],
"outputs": {
"bootstrapAsgId": {
"type": "string",
"value": "[resourceId('Microsoft.Network/applicationSecurityGroups', variables('bootstrapAsg'))]"
},
"bootstrapSubnetId": {
"type": "string",
"value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('vnet'), variables('bootstrapSubnet'))]"
},
"paasSubnetId": {
"type": "string",
"value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('vnet'), variables('paasSubnet'))]"
},
"laboratorySubnetId": {
"type": "string",
"value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('vnet'), variables('laboratorySubnet'))]"
},
"lockdownSubnetId": {
"type": "string",
"value": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('vnet'), variables('lockdownSubnet'))]"
},
"privateDnsId": {
"type": "string",
"value": "[resourceId('Microsoft.Network/privateDnsZones', variables('privateDns'))]"
},
"vnetId": {
"type": "string",
"value": "[resourceId('Microsoft.Network/virtualNetworks', variables('vnet'))]"
},
"workerAsgId": {
"type": "string",
"value": "[resourceId('Microsoft.Network/applicationSecurityGroups', variables('workerAsg'))]"
}
}
}

Просмотреть файл

@ -0,0 +1,220 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"laboratoryRegistryId": {
"type": "string"
},
"laboratorySqlServerId": {
"type": "string"
},
"laboratoryStorageAccountId": {
"type": "string"
},
"subnetId": {
"type": "string"
},
"vnetId": {
"type": "string"
}
},
"variables": {
"laboratoryRegistry": "[last(split(parameters('laboratoryRegistryId'), '/'))]",
"laboratorySqlServer": "[last(split(parameters('laboratorySqlServerId'), '/'))]",
"laboratoryStorageAccount": "[last(split(parameters('laboratoryStorageAccountId'), '/'))]",
"privateDnsZones": [
"privatelink.blob.core.windows.net",
"privatelink.queue.core.windows.net",
"privatelink.database.windows.net",
"privatelink.azurecr.io"
],
"privateEndpoints": [
{
"name": "laboratory-blob",
"dnsZone": "privatelink.blob.core.windows.net",
"dnsNames": [
"[variables('laboratoryStorageAccount')]"
],
"groupIds": [
"blob"
],
"resourceId": "[parameters('laboratoryStorageAccountId')]"
},
{
"name": "laboratory-queue",
"dnsZone": "privatelink.queue.core.windows.net",
"dnsNames": [
"[variables('laboratoryStorageAccount')]"
],
"groupIds": [
"queue"
],
"resourceId": "[parameters('laboratoryStorageAccountId')]"
},
{
"name": "laboratory-sql",
"dnsZone": "privatelink.database.windows.net",
"dnsNames": [
"[variables('laboratorySqlServer')]"
],
"groupIds": [
"sqlServer"
],
"resourceId": "[parameters('laboratorySqlServerId')]"
},
{
"name": "laboratory-acr",
"dnsZone": "privatelink.azurecr.io",
"dnsNames": [
"[format('{0}.{1}.data', variables('laboratoryRegistry'), tolower(resourceGroup().location))]",
"[variables('laboratoryRegistry')]"
],
"groupIds": [
"registry"
],
"resourceId": "[parameters('laboratoryRegistryId')]"
}
]
},
"resources": [
{
"copy": {
"name": "privateEndpoints",
"count": "[length(variables('privateEndpoints'))]"
},
"type": "Microsoft.Network/privateEndpoints",
"apiVersion": "2019-11-01",
"location": "[resourceGroup().location]",
"name": "[variables('privateEndpoints')[copyIndex()].name]",
"properties": {
"privateLinkServiceConnections": [
{
"name": "[variables('privateEndpoints')[copyIndex()].name]",
"properties": {
"groupIds": "[variables('privateEndpoints')[copyIndex()].groupIds]",
"privateLinkServiceId": "[variables('privateEndpoints')[copyIndex()].resourceId]"
}
}
],
"subnet": {
"id": "[parameters('subnetId')]"
}
}
},
{
"copy": {
"name": "privateDnsZones",
"count": "[length(variables('privateDnsZones'))]"
},
"type": "Microsoft.Network/privateDnsZones",
"apiVersion": "2018-09-01",
"location": "global",
"name": "[variables('privateDnsZones')[copyIndex()]]",
"properties": {
},
"resources": [
{
"dependsOn": [
"[resourceId('Microsoft.Network/privateDnsZones', variables('privateDnsZones')[copyIndex()])]"
],
"type": "SOA",
"apiVersion": "2018-09-01",
"name": "@",
"properties": {
"ttl": 3600,
"soaRecord": {
"email": "azureprivatedns-host.microsoft.com",
"expireTime": 2419200,
"host": "azureprivatedns.net",
"refreshTime": 3600,
"retryTime": 300,
"serialNumber": 1,
"minimumTtl": 300
}
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Network/privateDnsZones', variables('privateDnsZones')[copyIndex()])]"
],
"type": "virtualNetworkLinks",
"apiVersion": "2018-09-01",
"location": "global",
"name": "privatedns",
"properties": {
"registrationEnabled": false,
"virtualNetwork": {
"id": "[parameters('vnetId')]",
"resourceGroup": "[resourceGroup().name]"
}
}
}
]
},
{
"copy": {
"name": "privateDnsRecords",
"count": "[length(variables('privateEndpoints'))]"
},
"dependsOn": [
"[resourceId('Microsoft.Network/privateDnsZones', variables('privateEndpoints')[copyIndex()].dnsZone)]",
"[resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpoints')[copyIndex()].name)]"
],
"type": "Microsoft.Resources/deployments",
"apiVersion": "2017-05-10",
"name": "[concat('private-dns-', variables('privateEndpoints')[copyIndex()].name)]",
"resourceGroup": "[resourceGroup().name]",
"properties": {
"mode": "Incremental",
"expressionEvaluationOptions": {
"scope": "inner"
},
"parameters": {
"privateDnsZoneName": {
"value": "[variables('privateEndpoints')[copyIndex()].dnsZone]"
},
"endpointNicId": {
"value": "[reference(resourceId('Microsoft.Network/privateEndpoints', variables('privateEndpoints')[copyIndex()].name)).networkInterfaces[0].id]"
},
"endpointDnsNames": {
"value": "[variables('privateEndpoints')[copyIndex()].dnsNames]"
}
},
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"privateDnsZoneName": {
"type": "string"
},
"endpointNicId": {
"type": "string"
},
"endpointDnsNames": {
"type": "array"
}
},
"resources": [
{
"copy": {
"name": "endpointDnsName",
"count": "[length(parameters('endpointDnsNames'))]"
},
"type": "Microsoft.Network/privateDnsZones/A",
"apiVersion": "2018-09-01",
"name": "[concat(parameters('privateDnsZoneName'), '/', parameters('endpointDnsNames')[copyIndex()])]",
"properties": {
"ttl": 3600,
"aRecords": [
{
"ipv4Address": "[reference(parameters('endpointNicId'), '2019-11-01').ipConfigurations[copyIndex()].properties.privateIPAddress]"
}
]
}
}
]
}
}
}
]
}

12
deploy/azure/test.json Normal file
Просмотреть файл

@ -0,0 +1,12 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
],
"outputs": {
"templateLinkUri": {
"type": "string",
"value": "[if(contains(deployment().properties, 'templateLink'), deployment().properties.templateLink.uri, 'ERROR: templateLink does not exist!')]"
}
}
}

102
deploy/azure/worker.vm.json Normal file
Просмотреть файл

@ -0,0 +1,102 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"asgId": {
"type": "string"
},
"identityId": {
"type": "string"
},
"subnetId": {
"type": "string"
},
"imageId": {
"type": "string",
"defaultValue": "[resourceId('Microsoft.Compute/images', 'dct-worker')]"
},
"vmPassword": {
"type": "securestring",
"defaultValue": "[newGuid()]"
},
"vmSize": {
"type": "string",
"defaultValue": "Standard_DS2_v2"
}
},
"variables": {
"name": "worker"
},
"resources": [
{
"type": "Microsoft.Network/networkInterfaces",
"apiVersion": "2019-11-01",
"location": "[resourceGroup().location]",
"name": "[variables('name')]",
"properties": {
"ipConfigurations": [
{
"name": "default",
"properties": {
"applicationSecurityGroups": [
{
"id": "[parameters('asgId')]"
}
],
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[parameters('subnetId')]"
}
}
}
]
}
},
{
"dependsOn": [
"[resourceId('Microsoft.Network/networkInterfaces', variables('name'))]"
],
"type": "Microsoft.Compute/virtualMachines",
"apiVersion": "2019-07-01",
"location": "[resourceGroup().location]",
"name": "[variables('name')]",
"identity": {
"type": "UserAssigned",
"userAssignedIdentities": {
"[parameters('identityId')]": {
}
}
},
"properties": {
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('name'))]"
}
]
},
"osProfile": {
"adminUsername": "azureuser",
"adminPassword": "[parameters('vmPassword')]",
"computerName": "[variables('name')]",
"linuxConfiguration": {
}
},
"storageProfile": {
"imageReference": {
"id": "[parameters('imageId')]"
},
"osDisk": {
"createOption": "FromImage",
"managedDisk": {
"storageAccountType": "Premium_LRS"
}
}
}
}
}
]
}

Просмотреть файл

@ -0,0 +1,58 @@
#!/usr/bin/env pwsh
$ErrorActionPreference='Stop'
# Retrieve settings from DNS TXT record
$settings = @{}
$(dig '@168.63.129.16' +short laboratory.environment.private txt).Split("\n") | % {
$parts = $_.Substring(1, $_.Length-2)
$delimiter = $parts.IndexOf("=")
$key = $parts.Substring(0, $delimiter)
$value = $parts.Substring($delimiter + 1)
$settings[$key] = $value
}
$server = $settings["sqlServer"]
$database = $settings["sqlDatabase"]
$identity = $settings["identity"]
$clientId = $settings["clientId"]
$storageAccount = $settings["storageAccount"]
$settings["runsQueueEndpoint"] -match 'https://(?<storageAccount>\w+)\..*/(?<queue>\w+)'
$runsQueueStorageAccount = $matches["storageAccount"]
$runsQueue = $matches["queue"]
# Configure storage
az storage queue create --auth-mode login --account-name $runsQueueStorageAccount -n $runsQueue
if ($LastExitCode -ne 0) {
exit $LastExitCode
}
# Connect to Azure SQL via AAD (Managed Identity)
$token = curl -s 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://database.windows.net/' -H 'Metadata: true' | ConvertFrom-Json;
$conn = New-Object System.Data.SqlClient.SqlConnection;
$conn.ConnectionString = "Data Source=$server; Initial Catalog=$database;";
$conn.AccessToken = $token.access_token;
$conn.Open()
Write-Output "Connected to $server/$database using Managed Identity"
# Grant AAD permissions in Azure SQL
foreach ($byte in [System.Guid]::Parse($clientId).ToByteArray()) { $byteGuid += [System.String]::Format("{0:X2}", $byte) }
$sql = @"
if not exists (select name from sys.database_principals where name = '$identity')
begin
create user [$identity] with default_schema=[dbo], SID=0x$byteGuid, TYPE=E;
end
alter role db_ddladmin add member [$identity];
alter role db_datareader add member [$identity];
alter role db_datawriter add member [$identity];
"@
$cmd = New-Object System.Data.SqlClient.SqlCommand($sql, $conn)
$cmd.ExecuteNonQuery()
Write-Output "Added $identity as an AAD user to $server/$database"
$conn.Close()
# Deallocate bootstrap VM (self)
$vmId = $(curl -s 'http://169.254.169.254/metadata/instance/compute/resourceId?api-version=2019-08-15&format=text' -H 'Metadata: true')
az vm deallocate --no-wait --ids $vmId

14
deploy/bootstrap/setup.sh Normal file
Просмотреть файл

@ -0,0 +1,14 @@
#!/bin/bash
set -euxo pipefail
# Repositories
curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
apt-add-repository "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $(lsb_release -cs) main"
apt-add-repository "deb [arch=amd64] https://packages.microsoft.com/ubuntu/$(lsb_release -rs)/prod $(lsb_release -cs) main"
# Install tools
apt-get update -y
apt-get install -y azure-cli powershell
# Configure storage
az login --identity --allow-no-subscriptions

112
deploy/deploy.sh Executable file
Просмотреть файл

@ -0,0 +1,112 @@
#!/bin/bash
set -eo pipefail
show_usage() {
echo 'Usage: deploy.sh -g <resource_group> [--assets <assets_base_uri>] [--dev] [--force]'
}
parse_arguments() {
PARAMS=""
while (( $# )); do
case "$1" in
-h|--help)
show_usage
exit 0
;;
-f|--force)
FORCE=true
shift
;;
-g|--resource-group)
RESOURCE_GROUP=$2
shift 2
;;
-a|--assets)
ASSETS_BASE=$2
shift 2
;;
--dev)
DEV=true
shift
;;
--)
shift
break
;;
-*|--*)
echo "Unsupported flag $1" >&2
exit 1
;;
*)
PARAMS="$PARAMS $1"
shift
;;
esac
done
}
validate_arguments() {
if [[ -z "$RESOURCE_GROUP" ]]; then
show_usage
exit 1
fi
if [[ -z "$ASSETS_BASE" && "$DEV" = true ]]; then
ASSETS_BASE=$(curl -s http://127.0.0.1:4040/api/tunnels | jq -r '.tunnels[] | select(.name == "deploy").public_url')
fi
ASSETS_BASE=${ASSETS_BASE:-'https://raw.githubusercontent.com/microsoft/data-contest-toolkit/master/deploy'}
FORCE=${FORCE:-false}
}
deploy_environment() {
if ! az image show -g $RESOURCE_GROUP -n dct-worker &>/dev/null || [ "$FORCE" = true ]; then
TMP_DIR=$(mktemp -d)
(
pushd $TMP_DIR
curl -sL -O "${ASSETS_BASE}/worker/packer.json" -O "${ASSETS_BASE}/worker/setup.sh" -O "${ASSETS_BASE}/worker/start.sh"
packer build -force -var resource_group=$RESOURCE_GROUP packer.json
)
else
>&2 echo "Skipping worker VM image. Run with --force to recreate"
fi
az deployment group create -g $RESOURCE_GROUP -u "${ASSETS_BASE}/azure/azuredeploy.json" -p "assetsBaseUrl=$ASSETS_BASE"
}
deploy_laboratory() {
SITE_ID=$(az deployment group show -g $RESOURCE_GROUP -n azuredeploy --query properties.outputs.laboratorySiteId.value -o tsv)
npm run laboratory:package:appservice
az webapp deployment source config-zip --ids $SITE_ID --src dist/data-contest-toolkit.zip
az webapp restart --ids $SITE_ID
}
deploy_worker() {
REGISTRY=$(az deployment group show -g $RESOURCE_GROUP -n azuredeploy --query properties.outputs.laboratoryRegistryName.value -o tsv)
az acr import -n $REGISTRY --source docker.io/acanthamoeba/dct-worker:latest -t worker:latest --force
}
deploy_dev() {
if [ "$DEV" = true ]; then
az vm wait -g $RESOURCE_GROUP -n bootstrap --custom "instanceView.statuses[?code=='PowerState/deallocated']"
az vm start -g $RESOURCE_GROUP -n bootstrap
az deployment group create -g $RESOURCE_GROUP -u "${ASSETS_BASE}/azure/dev.json" -p "sshPublicKey=$(cat ~/.ssh/id_rsa.pub)"
fi
}
parse_arguments "$@"
validate_arguments
if [ -f .env ]; then
set -o allexport
source .env
set +o allexport
fi
set -ux
deploy_environment
deploy_worker
deploy_laboratory
deploy_dev

55
deploy/worker/packer.json Normal file
Просмотреть файл

@ -0,0 +1,55 @@
{
"variables": {
"name": "dct-worker",
"resource_group": null,
"tenant_id": "{{env `AZURE_TENANT_ID`}}",
"subscription_id": "{{env `AZURE_SUBSCRIPTION_ID`}}",
"client_id": "{{env `AZURE_CLIENT_ID`}}",
"client_secret": "{{env `AZURE_CLIENT_SECRET`}}",
"location": "southcentralus"
},
"sensitive-variables": [
"client_secret"
],
"builders": [
{
"type": "azure-arm",
"async_resourcegroup_delete": true,
"tenant_id": "{{user `tenant_id`}}",
"subscription_id": "{{user `subscription_id`}}",
"client_id": "{{user `client_id`}}",
"client_secret": "{{user `client_secret`}}",
"location": "{{user `location`}}",
"os_type": "Linux",
"image_publisher": "Canonical",
"image_offer": "UbuntuServer",
"image_sku": "18.04-LTS",
"managed_image_name": "{{user `name`}}",
"managed_image_resource_group_name": "{{user `resource_group`}}"
}
],
"provisioners": [
{
"type": "file",
"source": "{{template_dir}}/start.sh",
"destination": "/tmp/start.sh"
},
{
"type": "shell",
"execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E bash '{{ .Path }}'",
"script": "{{template_dir}}/setup.sh"
},
{
"type": "shell",
"execute_command": "chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh '{{ .Path }}'",
"inline_shebang": "/bin/sh -x",
"inline": [
"/usr/sbin/waagent -force -deprovision+user && export HISTSIZE=0 && sync"
]
}
]
}

18
deploy/worker/setup.sh Normal file
Просмотреть файл

@ -0,0 +1,18 @@
#!/bin/bash
set -euo pipefail
# Docker repository
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# Microsoft repository
curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
apt-add-repository "deb [arch=amd64] https://packages.microsoft.com/ubuntu/$(lsb_release -rs)/prod $(lsb_release -cs) main"
# Install dependencies
apt-get update -y
apt-get install -y jq docker-ce docker-ce-cli containerd.io blobfuse fuse
# Move startup script
chmod +x /tmp/start.sh
mv /tmp/start.sh /var/lib/cloud/scripts/per-boot/start.sh

36
deploy/worker/start.sh Normal file
Просмотреть файл

@ -0,0 +1,36 @@
#!/bin/bash
set -euo pipefail
# service discovery via DNS
SETTINGS=$(dig @168.63.129.16 +short laboratory.environment.private txt)
# blobfuse: one-time setup
mkdir -p /mnt/blobfusetmp
# blobfuse: mount runs container
mkdir -p /var/dct/runs
export AZURE_STORAGE_ACCOUNT=$(grep -Po 'storageAccount=\K[^"]*' <<< $SETTINGS)
export AZURE_STORAGE_AUTH_TYPE=MSI
CONTAINER=$(grep -Po 'runsContainer=\K[^"]*' <<< $SETTINGS)
blobfuse /var/dct/runs --container-name=$CONTAINER --tmp-path=/mnt/blobfusetmp
# pull latest worker
AAD_ACCESS_TOKEN=$(curl -s -H 'Metadata: true' 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/' | jq -r '.access_token')
CLAIMS=$(echo $AAD_ACCESS_TOKEN | cut -d '.' -f 2)
TENANT=$(echo $CLAIMS | base64 -d | jq -r '.iss | capture("https://(.*)/(?<tenant>.*)/").tenant')
REGISTRY=$(grep -Po 'registry=\K\K[^"]*' <<< $SETTINGS)
ACR_REFRESH_TOKEN=$(curl -s -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d "grant_type=access_token&service=$REGISTRY&tenant=$TENANT&access_token=$AAD_ACCESS_TOKEN" https://$REGISTRY/oauth2/exchange | jq -r '.refresh_token')
docker login -u 00000000-0000-0000-0000-000000000000 -p $ACR_REFRESH_TOKEN $REGISTRY
# If the pull was unsuccessful (not yet provisioned, network blip, etc), wait a bit and keep trying
while ! docker pull $REGISTRY/worker; do
sleep 10
done
# run worker app
QUEUE_ENDPOINT=$(grep -Po 'runsQueueEndpoint=\K[^"]*' <<< $SETTINGS)
docker container rm worker --force || true
docker container run -d --name worker --restart always -v /var/run/docker.sock:/var/run/docker.sock -e QUEUE_MODE=azure -e QUEUE_ENDPOINT=$QUEUE_ENDPOINT $REGISTRY/worker

809
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -16,32 +16,35 @@
"posttest": "npm run check",
"laboratory": "node build/src/laboratory/server/main.js",
"laboratory:dev": "npm run compile && node -r dotenv/config build/src/laboratory/server/main.js",
"laboratory:package:appservice": "npm run compile && mkdir -p dist && cp -r --parents package*.json build/src dist && cd dist && npm install --ignore-scripts --production && zip -r $npm_package_name.zip package.json package-lock.json build/src node_modules",
"prelaboratory:package:appservice": "rimraf ./dist",
"laboratory:package:appservice": "npm run compile && mkdir -p dist && cp -r --parents package*.json build/src dist && cd dist && npm install --ignore-scripts --only=prod && zip -r $npm_package_name.zip package.json package-lock.json build/src node_modules",
"sds": "node build/src/cli/sds.js",
"sds:dev": "npm run compile && node -r dotenv/config build/src/cli/sds.js",
"worker": "node build/src/worker/app.js",
"worker:dev": "npm run compile && node -r dotenv/config build/src/worker/app.js"
"worker:dev": "npm run compile && node -r dotenv/config build/src/worker/app.js",
"preazure:dev": "serve -l 8080 deploy >/dev/null &",
"azure:dev": "ngrok start -config .ngrok.yml deploy",
"postazure:dev": "kill $(lsof -t -i:8080)"
},
"dependencies": {
"@azure/identity": "^1.0.2",
"@azure/identity": "^1.0.3",
"@azure/storage-queue": "^12.0.4",
"axios": "^0.19.2",
"body-parser": "^1.19.0",
"commander": "^5.0.0",
"dockerode": "^3.1.0",
"dotenv": "^8.2.0",
"env-var": "^6.0.4",
"commander": "^5.1.0",
"dockerode": "^3.2.0",
"env-var": "^6.1.1",
"express": "^4.17.1",
"fp-ts": "^2.5.3",
"io-ts": "^2.1.2",
"fp-ts": "^2.5.4",
"io-ts": "^2.2.2",
"js-yaml": "^3.13.1",
"luxon": "^1.22.2",
"luxon": "^1.24.1",
"reflect-metadata": "^0.1.13",
"sequelize": "^5.21.5",
"sequelize": "^5.21.7",
"sequelize-typescript": "^1.1.0",
"sqlite3": "^4.1.1",
"sqlite3": "^4.2.0",
"strong-error-handler": "^3.4.0",
"tedious": "^8.2.0",
"tedious": "^8.3.0",
"uuid": "^3.4.0"
},
"devDependencies": {
@ -52,31 +55,34 @@
"@types/chai": "^4.2.11",
"@types/chai-as-promised": "^7.1.2",
"@types/chai-http": "^4.2.0",
"@types/dockerode": "^2.5.26",
"@types/dockerode": "^2.5.28",
"@types/dotenv": "^8.2.0",
"@types/express": "^4.17.3",
"@types/express": "^4.17.6",
"@types/js-yaml": "^3.12.3",
"@types/luxon": "^1.22.0",
"@types/mocha": "^7.0.2",
"@types/nock": "^11.1.0",
"@types/node": "^12.12.31",
"@types/node": "^12.12.37",
"@types/request-promise": "^4.1.46",
"@types/sinon": "^7.5.2",
"@types/sinonjs__fake-timers": "^6.0.0",
"@types/sinonjs__fake-timers": "^6.0.1",
"@types/tedious": "^4.0.0",
"@types/uuid": "^3.4.7",
"@types/uuid": "^3.4.9",
"@types/validator": "^12.0.1",
"@types/yargs": "^15.0.4",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"chai-exclude": "^2.0.2",
"chai-http": "^4.3.0",
"dotenv": "^8.2.0",
"gts": "^1.1.2",
"mocha": "^7.1.1",
"mocha": "^7.1.2",
"ngrok": "^3.2.7",
"nock": "^12.0.3",
"rimraf": "^3.0.2",
"sinon": "^9.0.0",
"source-map-support": "^0.5.16",
"serve": "^11.3.0",
"sinon": "^9.0.2",
"source-map-support": "^0.5.19",
"typescript": "^3.8.3",
"typescript-json-schema": "^0.42.0",
"xmlhttprequest": "^1.8.0"