docker registry template embedded (#451)

This commit is contained in:
Javier Darsie 2019-09-30 18:08:11 -07:00 коммит произвёл vikasnav
Родитель 65a7a15145
Коммит ae9b7e764d
5 изменённых файлов: 303 добавлений и 263 удалений

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

@ -11,7 +11,6 @@ Make sure you take care of the following pre-requisites before you start the set
- `Ubuntu Server 16.04-LTS` was syndicated from Azure Stack's Marketplace by the operator - `Ubuntu Server 16.04-LTS` was syndicated from Azure Stack's Marketplace by the operator
- `Custom Script Extensions for Linux 2.0` was syndicated from Azure Stack's Marketplace by the operator - `Custom Script Extensions for Linux 2.0` was syndicated from Azure Stack's Marketplace by the operator
- You have access to a X.509 certificate in PFX format - You have access to a X.509 certificate in PFX format
- You can execute [htpasswd](https://httpd.apache.org/docs/2.4/programs/htpasswd.html) to generate the authorized users credentials
- You can [connect](https://docs.microsoft.com/en-us/azure-stack/user/azure-stack-powershell-configure-user) to the target Azure Stack instance using PowerShell - You can [connect](https://docs.microsoft.com/en-us/azure-stack/user/azure-stack-powershell-configure-user) to the target Azure Stack instance using PowerShell
## Setup ## Setup
@ -20,112 +19,17 @@ The following section details the required steps to perform before you deploy th
Once you went through the details, you should be able to tweak the [setup script](setup.ps1) and adjust it to your needs. Once you went through the details, you should be able to tweak the [setup script](setup.ps1) and adjust it to your needs.
### Basic Authentication using .htpasswd files
`htpasswd` is a small command-line utility that creates and updates text files (usually named `.htpasswd`) used to store user credentials for basic HTTP authentication.
An usage example is shown below (add flag `-c` to create file `.htpasswd`):
```bash
htpasswd -Bb .htpasswd my-user my-password
```
#### Anonymous access
To allow anonymous access to the registry, update the `docker run` command executed by the [CSE script](script.sh) **before** you start the [storage configuration](#storage-configuration) step.
Deleting the lines that set variables REGISTRY_AUTH, REGISTRY_AUTH_HTPASSWD_PATH AND REGISTRY_AUTH_HTPASSWD_REALM will disable basic authentication.
### Storage configuration ### Storage configuration
The template instructs the container registry to use the [Azure storage driver](https://docs.docker.com/registry/storage-drivers/azure/) to persist the container images in a local storage account blob container. The template instructs the container registry to use the [Azure storage driver](https://docs.docker.com/registry/storage-drivers/azure/) to persist the container images in a local storage account blob container.
We will also store the `.htpasswd` file in the same storage account to keep it secure and readily available when you need to upgrade your registry or guest OS to a new version. ### Key Vault configuration
You can use the PowerShell snipped below to automate the storage account setup process: The deployment template will instruct Azure Resource Manager to drop your certificate in the virtual machine's file system. User credentials should be stored as secrets in the same local Key Vault instance where the PFX certificate is stored.
```powershell ### Basic Authorization
# Set variables to match your environment
$location = "your-location"
$resourceGroup = "registry-rg"
$saName = "registry"
$saContainer = "images"
$tokenIni = Get-Date
$tokenEnd = $tokenIni.AddYears(1.0)
# Create resource group User credentials should be stored as secrets in the same local Key Vault instance where the PFX certificate is stored. This can be achieved using the web UI or the SDK.
Write-Host "Creating resource group:" $resourceGroup
New-AzureRmResourceGroup -Name $resourceGroup -Location $location | out-null
# Create storage account
Write-Host "Creating storage account:" $saName
$sa = New-AzureRmStorageAccount -ResourceGroupName $resourceGroup -AccountName $saName -Location $location -SkuName Premium_LRS -EnableHttpsTrafficOnly 1
$saKey = (Get-AzureRmStorageAccountKey -ResourceGroupName $resourceGroup -AccountName $saName)[0].Value
# Create blob container
Write-Host "Creating blob container:" $saContainer
Set-AzureRmCurrentStorageAccount -ResourceGroupName $resourceGroup -AccountName $saName | out-null
$container = New-AzureStorageContainer -Name $saContainer
# Upload the CSE script so the template can later fetch it during deployment
Write-Host "Uploading configuration script"
Set-AzureStorageBlobContent -Container $saContainer -File script.sh | out-null
$cseToken = New-AzureStorageBlobSASToken -Container $saContainer -Blob "script.sh" -Permission r -StartTime $tokenIni -ExpiryTime $tokenEnd
$cseUrl = $container.CloudBlobContainer.Uri.AbsoluteUri + "/script.sh" + $cseToken
# The CSE script needs the .htpasswd file to configure the container registry
Write-Host "Uploading .htpasswd file"
Set-AzureStorageBlobContent -Container $saContainer -File .htpasswd | out-null
# Get htpasswd download URL
$htpasswdToken = New-AzureStorageBlobSASToken -Container $saContainer -Blob .htpasswd -Permission r -StartTime $tokenIni -ExpiryTime $tokenEnd
$htpasswdUrl = $container.CloudBlobContainer.Uri.AbsoluteUri + "/.htpasswd" + $htpasswdToken
```
## Key Vault configuration
The deployment template will instruct Azure Resource Manager to drop your certificate in the virtual machine's file system.
The snippet below creates the Key Vault resource and uploads the .pfx certificate.
```powershell
$kvName = "certs"
$secretName = "registry"
$pfxPath = "cert.pfx"
$pfxPass = ""
# Create key vault enabled for deployment
Write-Host "Creating key vault:" $kvName
$kv = New-AzureRmKeyVault -ResourceGroupName $resourceGroup -VaultName $kvName -Location $location -Sku standard -EnabledForDeployment
# Serialize certificate
$fileContentBytes = get-content $pfxPath -Encoding Byte
$fileContentEncoded = [System.Convert]::ToBase64String($fileContentBytes)
$jsonObject = @"
{
"data": "$filecontentencoded",
"dataType" :"pfx",
"password": "$pfxPass"
}
"@
$jsonObjectBytes = [System.Text.Encoding]::UTF8.GetBytes($jsonObject)
$jsonEncoded = [System.Convert]::ToBase64String($jsonObjectBytes)
$secret = ConvertTo-SecureString -String $jsonEncoded -AsPlainText -Force
# Upload certificate as secret
Write-Host "Storing certificate in key vault:" $pfxPath
$kvSecret = Set-AzureKeyVaultSecret -VaultName $kvName -Name $secretName -SecretValue $secret
```
### Certificate thumbprint
Your certificate thumbprint is a required parameter of the ARM template. The CSE script uses that information to find the certificate in the virtual machine' file system.
Run the following snipped to generate the certificate thumbprint:
```powershell
Write-Host "Computing certificate thumbprint"
$tp = Get-PfxCertificate -FilePath $pfxPath
```
## Template deployment ## Template deployment
@ -145,7 +49,7 @@ New-AzureRmResourceGroupDeployment `
### Upgrade ### Upgrade
In order to upgrade the guest OS or the container registry itself, update [azuredeploy.json](azuredeploy.json) and/or [script.sh](script.sh) as needed and run once again `New-AzureRmResourceGroupDeployment` as previously indicated. In order to upgrade the guest OS or the container registry itself, update [azuredeploy.json](azuredeploy.json) as needed and run once again `New-AzureRmResourceGroupDeployment` as previously indicated.
## Usage ## Usage

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

@ -50,10 +50,29 @@
"description": "The guest OS image version." "description": "The guest OS image version."
} }
}, },
"storageAccountName": { "pipName": {
"type": "string", "type": "string",
"metadata": { "metadata": {
"description": "An already existing storage account name." "description": "The public IP resource name."
}
},
"pipDomainNameLabel": {
"type": "string",
"metadata": {
"description": "The public IP DNS label."
}
},
"pipAllocationMethod": {
"type": "string",
"defaultValue": "dynamic",
"metadata": {
"description": "The public IP allocation method."
}
},
"storageAccountResourceId": {
"type": "string",
"metadata": {
"description": "An already existing storage account resource identifier."
} }
}, },
"storageAccountContainer": { "storageAccountContainer": {
@ -62,31 +81,19 @@
"description": "An already existing storage account container name." "description": "An already existing storage account container name."
} }
}, },
"storageAccountKey": { "pfxKeyVaultResourceId": {
"type": "string",
"metadata": {
"description": "The storage account access key."
}
},
"domainNameLabel": {
"type": "string",
"metadata": {
"description": "The public IP DNS label."
}
},
"keyVaultResourceId": {
"type": "string", "type": "string",
"metadata": { "metadata": {
"description": "The Key Vault resource identifier." "description": "The Key Vault resource identifier."
} }
}, },
"keyVaultSecretUrl": { "pfxKeyVaultSecretUrl": {
"type": "string", "type": "string",
"metadata": { "metadata": {
"description": "Absolute URL to the Key Vault secret that stores the pfx certificate." "description": "Absolute URL to the Key Vault secret that stores the pfx certificate."
} }
}, },
"certificateThumbprint": { "pfxThumbprint": {
"type": "string", "type": "string",
"metadata": { "metadata": {
"description": "The certificate thumbprint." "description": "The certificate thumbprint."
@ -100,41 +107,47 @@
} }
}, },
"registryReplicas": { "registryReplicas": {
"type": "int", "type": "string",
"defaultValue": 20, "defaultValue": "20",
"metadata": { "metadata": {
"description": "Docker registry replicas." "description": "Docker registry replicas."
} }
}, },
"cseLocation": { "servicePrincipalClientId": {
"type": "string", "type": "securestring",
"metadata": { "metadata": {
"description": "Download URL of the script to be executed by the template custom script extension." "description": "Client ID with access to list and get secrets from the credentials Key Vault instance"
} }
}, },
"htpasswdLocation": { "servicePrincipalClientSecret": {
"type": "string", "type": "securestring",
"metadata": { "metadata": {
"description": "Download URL of the .htpasswd file." "description": "Secret of the client with access to list and get secrets from the credentials Key Vault instance"
} }
} }
}, },
"variables": { "variables": {
"vnetId": "[resourceId('Microsoft.Network/virtualNetworks', 'registry-vnet')]", "rgname": "[resourceGroup().name]",
"nsgName": "[concat(variables('rgname'), '-nsg')]",
"nicName": "[concat(variables('rgname'), '-nic')]",
"vnetName": "[concat(variables('rgname'), '-vnet')]",
"vnetId": "[resourceId('Microsoft.Network/virtualNetworks',variables('vnetName'))]",
"subnetRef": "[concat(variables('vnetId'), '/subnets/default')]", "subnetRef": "[concat(variables('vnetId'), '/subnets/default')]",
"tenantId": "[subscription().tenantId]",
"location": "[resourceGroup().location]", "location": "[resourceGroup().location]",
"provisionScriptParameters": "[concat('ADMIN_USER_NAME=', parameters('adminUsername'),' REGISTRY_STORAGE_AZURE_ACCOUNTNAME=', parameters('storageAccountName'),' REGISTRY_STORAGE_AZURE_ACCOUNTKEY=', parameters('storageAccountKey'),' REGISTRY_STORAGE_AZURE_CONTAINER=', parameters('storageAccountContainer'),' CERT_THUMBPRINT=', parameters('certificateThumbprint'),' PIP_LABEL=', parameters('domainNameLabel'),' REGISTRY_TAG=', parameters('registryTag'),' REGISTRY_REPLICAS=', parameters('registryReplicas'))]" "provisionScriptParameters": "[concat('ADMIN_USER_NAME=', parameters('adminUsername'),' SA_RESOURCE_ID=', parameters('storageAccountResourceId'),' SA_CONTAINER=', parameters('storageAccountContainer'),' KV_RESOURCE_ID=', parameters('pfxKeyVaultResourceId'),' CERT_THUMBPRINT=', parameters('pfxThumbprint'),' PIP_LABEL=', parameters('pipDomainNameLabel'),' REGISTRY_TAG=', parameters('registryTag'),' SPN_CLIENT_ID=',parameters('servicePrincipalClientId'),' SPN_CLIENT_SECRET=',parameters('servicePrincipalClientSecret'),' REGISTRY_REPLICAS=', parameters('registryReplicas'))]"
}, },
"resources": [ "resources": [
{ {
"type": "Microsoft.Compute/virtualMachines", "type": "Microsoft.Compute/virtualMachines",
"name": "registry-vm", "name": "[concat(variables('rgname'),'-vm')]",
"apiVersion": "2017-03-30", "apiVersion": "2017-03-30",
"location": "[resourceGroup().location]", "location": "[resourceGroup().location]",
"properties": { "properties": {
"osProfile": { "osProfile": {
"computerName": "registry", "computerName": "[concat(variables('rgname'),'-vm')]",
"adminUsername": "[parameters('adminUsername')]", "adminUsername": "[parameters('adminUsername')]",
"customData": "[base64(concat('#cloud-config\n\nwrite_files:\n- path: \"/opt/azure/containers/script.sh\"\n permissions: \"0744\"\n encoding: gzip\n owner: \"root\"\n content: !!binary |\n H4sIAAAAAAAA/+w6/XPbtpK/86/Y8HFqOzMkJTvOm1Me06MlxtFElnwinb6em+PAJCSxJgkaAO2osv73G4CfouSmXzO9H66dcejF7mK/dwH4H6/Muyg17xBbgf5VUZz53LevPX88dT17MvG98ZUzu/Gs/4Dmv3+AFyWY5ByilHEUx1G6BIof8ojiEFDGIUPBPVpiJtldjV13PL30h3PP/zCeOFa/B7DD7gKFEGDKga/y5C6jUcphNods8RVSInaBe7yGR5THEs5xksWIY0giFpB0ES1zsfHnK2A4oJgz8S+PSLqz/Sfnx3L7/t+x/Y3rzP3h3Bk5U29sT1yrf1puPyWQM0whoDjEKY9QzEpOsCB5GgJJYRk94pYYkvPcuRy73vxHfzrz/PnNdDqeXlr9s7abVhgCknIUpZgCxcuIcbqGBYpiHAInwDiiHFgeBJixRR7H60Lm2cWPMgomY9erQ+D0fD8AnlDEhfcXhEJC7tbS+4zkNKic7/qX14XxR7MfppOZPWoYvn2J1ZULl9eXUt+QPKUxQWEjWDc0T/95IDL3BCtDtWDjePbI9mzrbDcSGzYOpYTCQ47pWnBJMEch4qhS6Xo+G/kj52JfpzenLwix4jxjA9OsUsNIooASRhbcCEhiFoFk5nd5ynOz/9bovalR9RpVzygJjRDf7Qly/enSt0cj/4M9nlhvzko1PtSeRmEIFGcEsvslLKIY15l+cz2yPadJ9MOZ3lYFZVxfYg55Foo04AQCkmQx5lhRKOZ0HSShHy18EWc5xccnsFEEN7EWYWZp/XeSn89ijDNLO30HvNjG0s7egcRlq2jB4bvvOh9yTcgQibTUjhl+gD5oJeeTdxASpRK+5Ala/bH5z63gdEcxuofnZ/ipxo0WcAtaBDp+qLnBl3fAVzhV2qFBMc9pCv0aiGOGdzCkUqA1Ctari0h+hiTFylaRCAtCfZRxPybBPasN9bSKYgwLWRXMR0TNOLozw+x+aQq8BoQybsYR46wFD1CwwnIF0WAVPeJy8b0Z4kczzeMYTt9/19+xFA5WBI5+aLmY4hgjhoEsZEZL8Y6UXRXP2toIJZaY+0VM7Lm835O/CqwCwyc5z3JumTzJzDKg9GLJIPnv8vO+IeslYTPQ9bpKg47qtSqK9QXoTXmoVl/BcSfMhdXgGTjGoO3pAc+wpDgD3QH1f45vf3C+DIzXJ8/Ht9j5Qqnx+kRTT0ToNREXIH6Iz+HwPIz8F0Vv6c7zg3H6goEbh5eG+7uS/E86v5QedAKj7H45GMwy0bnZYGCpur4gNMCSAYlDFXQ9JXpJoVMckCTBachE+PzdpaVKriYFf583S61GOGuq0M3FzdS78efOxLFdx9KOY3bnV4VBp6CzE4knm/JnZ+6OZ1NLPTN6xltVqWOh0wygf9qDczg9hyCn8e/ritpmV6StKduhqIDwHmQl6XRKufT8DPhrxEF7cbR5WdgewLn4GWQv8zcxD2TBLeceCTVC809t/HusdI/XrJHMQCwQ5SgTwR9iRBNC98xjiOVd+V4e1P6AeST/2jCc5ozjUAD37fKNfXejepd2f3yRJDt0VYaf9uCsJ+2KMtEiT/WcRzGTw6GO02WUYkvbtGN5W6wFcbS3oOsojsmTLobTJUUhZgec3ZlTpVSipSckBB1dQkiCe0xB29ijq/G0OCBM7Stn2yhRTlt+6XhRDmVA/PzQKoLRAl7tqSt0hbe9XtE2SvpO0fmZ5DRFccDjorRlaIkp6PkOTVONdsx+SLnuhLPAPFjN7Jyv6priTEfXs/HUcy3tOLkXJ6mihkitdAa6LuMMzqsvPcQxWosY0/UEfdVFD4G3PdG1mxJb5UeCUrTECU65oW0+/NdoujWryd3EaZiRKOXse5RF+iOmLCKpddrrn+u9vt7rw3vQNrV426KAFbX7e9BTDL29ot3yd3mgUFpGkD9m9o330dKOf34QFdNAuWDAowCJLmPEZBmlTinYzvaFVeRcpm0kk209YmBQTRQumKZ2hMQPB4T0Zp+cqX8zn1hqxckkQo5Tk5N7nKrKXqs5QKJtPGdqTz1/PDpEvogqdw+bQ2zt9Lnjzm7mQ+dFO6A8jHAaYHbb+7JjBngGhkNQ2fMBF9cweSQ2ntWTwh5SfEs7/iMxpf8brmeu14ot/SOoQ5JynHLdW2d4ACjL4lJy86v+9PQk5oREz2mM04CEOFTb5CGoS4pS7vN1hq0gjnDK/dZRv4tcYkShpW3c66k/nIydwu47mLoI6mbPmq64ONildZ3h3PG+QU9x0b4sbVP5a4dC29RhIdxSOPLIQPLqwJexcFQ64NPnIngq/2ibT5+LymYUrqqSs7wvKWKokNL9g45rBFXldkLMbqa/1fs9vddXpU9FWSI0+kX6cQAXGFFZjKWWW7VW0XhEcY5vvxhRWKpHEzBWPEOMPYUSwEkerDowUbnLSxwxw25K7bZlfYRa3GJBmqcZorRNQWuaWqmLeojA0o7LEnGAi/BSkHMRVEfmkbDR6UmXyWd7cuP8aYuXVi9E/iutftKq8IVtQb+4ayy9q/i2+VXqtW16UVMnXwk1GwZ7JV2YU23WF+IgHjHAScbXRuOEclbXfu2Gr1MZXU4oWuJPeP1XVMb/r3X/N2qda+/WugMjiLZxbb/aRzZQcUAQcdBNlX+WqVJx/uT8+Jf5VpViyFLymzKxHQsTnC75agC9JkFVQ5w6bntfikxVT5StosjsmcyGtjeeTQdNcdA2FXBb4NTDxKCF00wYBVJnLB5IpL1ZWaLuGnhQ8OtYvcYczqaePZ4680G1cxtY4n36fIDjLrDEHDpzz/c+3lxdXM/HU68QswMsUa/H176IiR3TVMAWzsS+cCaDDo4Elkj11b9nX9ZatIFdvLlzPRkPbXfQxquAlWnaGTioTLOTlnuIRboN4PXr14ri2t2utOuBeoIlKzHFHt3eDlCc5sngy5fX2tGJUk4JDX3H3t+iV5x/e/5odmWPpwUjtbGuaWq1EQ2tCkcjiEkeoiwztqoi0KxWqBrapsNwqyj1I5a672QjoFxV6nemQxgZfVSVwmiWdkwynDIWA0VpCPodYvjtGzg7PVEU2av0BZgk4yb6JafYfFyF1eWTUd237/SvonehewaCE0SJPPGx+yjLonQJIc5wGuI0iDCr7r1k8qvVVKPTBZgrkmB50YBp0T9xLO+hW6AlJXkW4riC1YeH1i2SsojKevCI4ihEvCuCUrXjBaj1hfYTQkucclPbVHbeqrs6Nkeuzqui3PDXeVae+SbPCrFRAoWhUCDAlDPgRBBDgHTGCcVKkB3Ybogpjxaid2JmZDhRQTVzRs2YBCg22QpRbAZID1poJvqFtciKcCruPfQOailWQLJ1+Xgo3y8JJCRPOcgDpeJ6s7ljqfIahrHYlNKb1TOgqiT3YURBz0CTmIf12HGFaCICdbsDfomwZe82YQMutVhiLsOj6Z1Qn9ZblwgltgQIdFYMVHCP13sTVhe3+8CqfPSubdf9YeSPxnNLNSkh3BSzV9sqbZy9061SjIBDEmJL+15G3q283y2B8MoSp/H9QGtQZHgFWXuebW9pNieKQhv5Wlu8tZcvufXjrhIgDv/6lzP7AO/flzdLuqgShGFjncRKOWUM4OjsSGGYPkYBZgMFal6DIoNF0Rg0wG5XKSpCFpMSHyAhoSQo5kQcKtWALAHshYZTIUmV/IzEUVBzBKFWGBVzCUn18raxudcXU88Azoub/oxQzipSHdQ3b84G571er6hqjyTOE9xa302GQedXShrEOiQG8mfhwfQxoiQVIdqwrNWbzC59ezh0XFd8jcaufTFxRtYCNZcrLWyRD/alY8nq/uKyb//3zdwRbGc3U69oj5uy0W5/I5UcIzfFPPlNmnoQsrpz0TcI5449ubLKoXcfWV6E7RyR95b9Ovqvbe+jJc1uGr+VpthfnVe5MccoTtR9qo+ed+3bo9Hc6hnyfxkuL+BVrbo8WB7QS6J5E1cO6y+U2nbR+xUOYloYfxgPbc/5FU513VWc2QdFKW+R2ROiCURpxGsIR8F9maxNxdCDQ9VBKV90ewrLQ1LdTLM14ziBjOYpBh2Jtvpdp6kT0RTrvzBhHPGcKcPxyNKOSyYZq8Y2tRLCrz6M/k+GOFKsMAqFZP3TE8X1bO/GbeijlGU44GKmLkbAnx9ANcTBw+WiSbpyT8HlK6JLdlJW4leiqQlOYFmg0jxNo3Sp1vUYOp3/0N/SiPL8vwEAAP//y1cPrx8lAAA='))]",
"linuxConfiguration": { "linuxConfiguration": {
"disablePasswordAuthentication": true, "disablePasswordAuthentication": true,
"ssh": { "ssh": {
@ -149,11 +162,11 @@
"secrets": [ "secrets": [
{ {
"sourceVault": { "sourceVault": {
"id": "[parameters('keyVaultResourceId')]" "id": "[parameters('pfxKeyVaultResourceId')]"
}, },
"vaultCertificates": [ "vaultCertificates": [
{ {
"certificateUrl": "[parameters('keyVaultSecretUrl')]" "certificateUrl": "[parameters('pfxKeyVaultSecretUrl')]"
} }
] ]
} }
@ -181,29 +194,25 @@
"networkProfile": { "networkProfile": {
"networkInterfaces": [ "networkInterfaces": [
{ {
"id": "[resourceId('Microsoft.Network/networkInterfaces', 'registry-nic')]" "id": "[resourceId('Microsoft.Network/networkInterfaces',variables('nicName'))]"
} }
] ]
} }
}, },
"dependsOn": [ "dependsOn": [
"[concat('Microsoft.Network/networkInterfaces/', 'registry-nic')]" "[concat('Microsoft.Network/networkInterfaces/',variables('nicName'))]"
] ]
}, },
{ {
"apiVersion": "2017-03-30", "apiVersion": "2017-03-30",
"dependsOn": [ "dependsOn": [
"[concat('Microsoft.Compute/virtualMachines/', 'registry-vm')]" "[concat('Microsoft.Compute/virtualMachines/',variables('rgname'),'-vm')]"
], ],
"location": "[resourceGroup().location]", "location": "[resourceGroup().location]",
"name": "registry-vm/cse", "name": "[concat(variables('rgname'),'-vm/cse')]",
"properties": { "properties": {
"protectedSettings": { "protectedSettings": {
"fileUris": [ "commandToExecute": "[concat(variables('provisionScriptParameters'),' LOCATION=',variables('location'),' TENANT_ID=',variables('tenantId'),' PIP_FQDN=', '\"', reference(resourceId('Microsoft.Network/publicIPAddresses',parameters('pipName')),'2017-10-01').dnsSettings.fqdn,'\"',' /opt/azure/containers/script.sh >> /var/log/azure/docker-registry.log 2>&1')]"
"[parameters('cseLocation')]",
"[parameters('htpasswdLocation')]"
],
"commandToExecute": "[concat(variables('provisionScriptParameters'),' LOCATION=',variables('location'),' FQDN=', '\"', reference(resourceId('Microsoft.Network/publicIPAddresses', 'registry-pip'),'2017-10-01').dnsSettings.fqdn,'\"',' ./script.sh >> /var/log/azure/docker-registry.log 2>&1')]"
}, },
"publisher": "Microsoft.Azure.Extensions", "publisher": "Microsoft.Azure.Extensions",
"settings": {}, "settings": {},
@ -215,7 +224,7 @@
}, },
{ {
"type": "Microsoft.Network/virtualNetworks", "type": "Microsoft.Network/virtualNetworks",
"name": "registry-vnet", "name": "[concat(variables('rgname'),'-vnet')]",
"apiVersion": "2017-10-01", "apiVersion": "2017-10-01",
"location": "[resourceGroup().location]", "location": "[resourceGroup().location]",
"properties": { "properties": {
@ -236,7 +245,7 @@
}, },
{ {
"type": "Microsoft.Network/networkInterfaces", "type": "Microsoft.Network/networkInterfaces",
"name": "registry-nic", "name": "[variables('nicName')]",
"apiVersion": "2017-10-01", "apiVersion": "2017-10-01",
"location": "[resourceGroup().location]", "location": "[resourceGroup().location]",
"properties": { "properties": {
@ -249,19 +258,19 @@
}, },
"privateIPAllocationMethod": "Dynamic", "privateIPAllocationMethod": "Dynamic",
"publicIpAddress": { "publicIpAddress": {
"id": "[resourceId('Microsoft.Network/publicIpAddresses', 'registry-pip')]" "id": "[resourceId('Microsoft.Network/publicIpAddresses',parameters('pipName'))]"
} }
} }
} }
], ],
"networkSecurityGroup": { "networkSecurityGroup": {
"id": "[resourceId('Microsoft.Network/networkSecurityGroups', 'registry-nsg')]" "id": "[resourceId('Microsoft.Network/networkSecurityGroups',variables('nsgName'))]"
} }
}, },
"dependsOn": [ "dependsOn": [
"[concat('Microsoft.Network/virtualNetworks/', 'registry-vnet')]", "[concat('Microsoft.Network/virtualNetworks/',variables('rgname'),'-vnet')]",
"[concat('Microsoft.Network/publicIpAddresses/', 'registry-pip')]", "[concat('Microsoft.Network/publicIpAddresses/',parameters('pipName'))]",
"[concat('Microsoft.Network/networkSecurityGroups/', 'registry-nsg')]" "[concat('Microsoft.Network/networkSecurityGroups/',variables('nsgName'))]"
] ]
}, },
{ {
@ -269,19 +278,19 @@
"sku": { "sku": {
"name": "Basic" "name": "Basic"
}, },
"name": "registry-pip", "name": "[parameters('pipName')]",
"apiVersion": "2017-10-01", "apiVersion": "2017-10-01",
"location": "[resourceGroup().location]", "location": "[resourceGroup().location]",
"properties": { "properties": {
"publicIpAllocationMethod": "Dynamic", "publicIpAllocationMethod": "[parameters('pipAllocationMethod')]",
"dnsSettings": { "dnsSettings": {
"domainNameLabel": "[parameters('domainNameLabel')]" "domainNameLabel": "[parameters('pipDomainNameLabel')]"
} }
} }
}, },
{ {
"type": "Microsoft.Network/networkSecurityGroups", "type": "Microsoft.Network/networkSecurityGroups",
"name": "registry-nsg", "name": "[variables('nsgName')]",
"apiVersion": "2017-10-01", "apiVersion": "2017-10-01",
"location": "[resourceGroup().location]", "location": "[resourceGroup().location]",
"properties": { "properties": {

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

@ -2,34 +2,61 @@
"schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", "schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0", "contentVersion": "1.0.0.0",
"parameters": { "parameters": {
"storageAccountName": { "adminUsername": {
"value": ""
},
"storageAccountContainer": {
"value": ""
},
"storageAccountKey": {
"value": ""
},
"keyVaultResourceId": {
"value": ""
},
"keyVaultSecretUrl": {
"value": ""
},
"certificateThumbprint": {
"value": "" "value": ""
}, },
"adminPublicKey": { "adminPublicKey": {
"value": "" "value": ""
}, },
"domainNameLabel": { "virtualMachineSize": {
"value": "" "value": ""
}, },
"cseLocation": { "virtualMachinePublisher": {
"value": "" "value": ""
}, },
"htpasswdLocation": { "virtualMachineOffer": {
"value": ""
},
"virtualMachineSku": {
"value": ""
},
"virtualMachineVersion": {
"value": ""
},
"pipName": {
"value": ""
},
"pipDomainNameLabel": {
"value": ""
},
"pipAllocationMethod": {
"value": ""
},
"storageAccountResourceId": {
"value": ""
},
"storageAccountContainer": {
"value": ""
},
"pfxKeyVaultResourceId": {
"value": ""
},
"pfxKeyVaultSecretUrl": {
"value": ""
},
"pfxThumbprint": {
"value": ""
},
"registryTag": {
"value": ""
},
"registryReplicas": {
"value": ""
},
"servicePrincipalClientId": {
"value": ""
},
"servicePrincipalClientSecret": {
"value": "" "value": ""
} }
} }

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

@ -1,18 +1,25 @@
#!/bin/bash -x #!/bin/bash
#
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license.
ERR_APT_INSTALL_TIMEOUT=9 # Timeout installing required apt packages # ===
ERR_MISSING_CRT_FILE=10 # Bad cert thumbprint OR pfx not in key vault OR template misconfigured VM secrets section # NOTE: the CSE script is embedded in azuredeploy.json (base64 encoding)
ERR_MISSING_KEY_FILE=11 # Bad cert thumbprint OR pfx not in key vault OR template misconfigured VM secrets section # Update the VM resource (.properties.osPrifile.customData) to alter the CSE script.
ERR_REGISTRY_NOT_RUNNING=13 # the container registry failed to start successfully # ===
ERR_MOBY_APT_LIST_TIMEOUT=25 # Timeout waiting for moby apt sources
ERR_MS_GPG_KEY_DOWNLOAD_TIMEOUT=26 # Timeout waiting for MS GPG key download ERR_APT_INSTALL_TIMEOUT=9 # Timeout installing required apt packages
ERR_MOBY_INSTALL_TIMEOUT=27 # Timeout waiting for moby install ERR_MISSING_CRT_FILE=10 # Bad cert thumbprint OR pfx not in key vault OR template misconfigured VM secrets section
ERR_MISSING_KEY_FILE=11 # Bad cert thumbprint OR pfx not in key vault OR template misconfigured VM secrets section
ERR_MISSING_USER_CREDENTIALS=12 # No user credentials secret found on given key vault
ERR_REGISTRY_NOT_RUNNING=13 # The container registry failed to start successfully
ERR_MOBY_APT_LIST_TIMEOUT=25 # Timeout waiting for moby apt sources
ERR_MS_GPG_KEY_DOWNLOAD_TIMEOUT=26 # Timeout waiting for MS GPG key download
ERR_MOBY_INSTALL_TIMEOUT=27 # Timeout waiting for moby install
ERR_METADATA=30 # Error querying metadata
ERR_MS_PROD_DEB_DOWNLOAD_TIMEOUT=42 # Timeout waiting for https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb ERR_MS_PROD_DEB_DOWNLOAD_TIMEOUT=42 # Timeout waiting for https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb
ERR_MS_PROD_DEB_PKG_ADD_FAIL=43 # Failed to add repo pkg file ERR_MS_PROD_DEB_PKG_ADD_FAIL=43 # Failed to add repo pkg file
ERR_APT_UPDATE_TIMEOUT=99 # Timeout waiting for apt-get update to complete ERR_APT_UPDATE_TIMEOUT=99 # Timeout waiting for apt-get update to complete
UBUNTU_RELEASE=$(lsb_release -r -s)
MOBY_VERSION="3.0.6"
retrycmd_if_failure() { retrycmd_if_failure() {
retries=$1; wait_sleep=$2; timeout=$3; retries=$1; wait_sleep=$2; timeout=$3;
@ -66,13 +73,16 @@ apt_get_install() {
wait_for_apt_locks wait_for_apt_locks
} }
installDeps() { installDeps() {
UBUNTU_RELEASE=$(lsb_release -r -s)
MOBY_VERSION="3.0.6"
retrycmd_if_failure 120 5 25 curl https://packages.microsoft.com/config/ubuntu/${UBUNTU_RELEASE}/prod.list > /tmp/microsoft-prod.list || exit $ERR_MOBY_APT_LIST_TIMEOUT retrycmd_if_failure 120 5 25 curl https://packages.microsoft.com/config/ubuntu/${UBUNTU_RELEASE}/prod.list > /tmp/microsoft-prod.list || exit $ERR_MOBY_APT_LIST_TIMEOUT
retrycmd_if_failure 10 5 10 cp /tmp/microsoft-prod.list /etc/apt/sources.list.d/ || exit $ERR_MOBY_APT_LIST_TIMEOUT retrycmd_if_failure 10 5 10 cp /tmp/microsoft-prod.list /etc/apt/sources.list.d/ || exit $ERR_MOBY_APT_LIST_TIMEOUT
retrycmd_if_failure 120 5 25 curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/microsoft.gpg || exit $ERR_MS_GPG_KEY_DOWNLOAD_TIMEOUT retrycmd_if_failure 120 5 25 curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > /tmp/microsoft.gpg || exit $ERR_MS_GPG_KEY_DOWNLOAD_TIMEOUT
retrycmd_if_failure 10 5 10 cp /tmp/microsoft.gpg /etc/apt/trusted.gpg.d/ || exit $ERR_MS_GPG_KEY_DOWNLOAD_TIMEOUT retrycmd_if_failure 10 5 10 cp /tmp/microsoft.gpg /etc/apt/trusted.gpg.d/ || exit $ERR_MS_GPG_KEY_DOWNLOAD_TIMEOUT
apt_get_update || exit $ERR_APT_UPDATE_TIMEOUT apt_get_update || exit $ERR_APT_UPDATE_TIMEOUT
apt_get_install 20 30 120 moby-engine=${MOBY_VERSION} moby-cli=${MOBY_VERSION} --allow-downgrades || exit $ERR_MOBY_INSTALL_TIMEOUT apt_get_install 20 30 120 apache2-utils moby-engine=${MOBY_VERSION} moby-cli=${MOBY_VERSION} --allow-downgrades || exit $ERR_MOBY_INSTALL_TIMEOUT
usermod -aG docker ${ADMIN_USER_NAME} usermod -aG docker ${ADMIN_USER_NAME}
for apt_package in curl jq; do for apt_package in curl jq; do
@ -82,26 +92,97 @@ installDeps() {
fi fi
done done
} }
fetchOAuth() {
ENDPOINTS=$(mktemp)
curl -s --retry 5 --retry-delay 10 --max-time 60 -f \
https://management.${FQDN}/metadata/endpoints?api-version=2015-01-01 > ${ENDPOINTS}
echo ADMIN_USER_NAME: ${ADMIN_USER_NAME} if [ $? -ne 0 ]; then
echo REGISTRY_STORAGE_AZURE_ACCOUNTNAME: ${REGISTRY_STORAGE_AZURE_ACCOUNTNAME} exit $ERR_METADATA
echo REGISTRY_STORAGE_AZURE_ACCOUNTKEY: ${REGISTRY_STORAGE_AZURE_ACCOUNTKEY} fi
echo REGISTRY_STORAGE_AZURE_CONTAINER: ${REGISTRY_STORAGE_AZURE_CONTAINER}
echo CERT_THUMBPRINT: ${CERT_THUMBPRINT} OAUTH=$(jq -r .authentication.loginEndpoint ${ENDPOINTS})
echo FQDN: ${FQDN} echo ${OAUTH} | grep -e "/adfs$"
echo LOCATION: ${LOCATION}
echo PIP_LABEL: ${PIP_LABEL} if [ $? -eq 0 ]; then
echo REGISTRY_TAG: ${REGISTRY_TAG} TOKEN_URL="${OAUTH}/oauth2/token"
echo REGISTRY_REPLICAS: ${REGISTRY_REPLICAS} else
TOKEN_URL="${OAUTH}${TENANT_ID}/oauth2/token"
fi
}
fetchCredentials() {
RESOURCE=$(jq -r .authentication.audiences[0] ${ENDPOINTS} | sed "s|https://management.|https://vault.|")
TOKEN=$(curl -s --retry 5 --retry-delay 10 --max-time 60 -f -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=${SPN_CLIENT_ID}" \
--data-urlencode "client_secret=${SPN_CLIENT_SECRET}" \
--data-urlencode "resource=${RESOURCE}" \
${TOKEN_URL} | jq -r '.access_token')
KV_URL="https://${KV_NAME}.vault.${FQDN}/secrets"
SECRETS=$(curl -s --retry 5 --retry-delay 10 --max-time 60 -f \
"${KV_URL}?api-version=2016-10-01" -H "Authorization: Bearer ${TOKEN}" | jq -r .value[].id)
rm .htpasswd
touch .htpasswd
for secret in ${SECRETS}
do
SECRET_NAME_VERSION="${secret//$KV_URL}"
SECRET_NAME=$(echo ${SECRET_NAME_VERSION} | cut -d '/' -f 2)
SECRET_VALUE=$(curl -s --retry 5 --retry-delay 10 --max-time 60 -f \
"${secret}?api-version=2016-10-01" -H "Authorization: Bearer ${TOKEN}" | jq -r .value)
htpasswd -Bb .htpasswd ${SECRET_NAME} ${SECRET_VALUE}
done
if [ ! -s .htpasswd ]; then
echo "file .htpasswd is empty, credentials were not created or there was an error fetching credentials from keyvault"
exit $ERR_MISSING_USER_CREDENTIALS
fi
}
fetchStorageKeys() {
RESOURCE=$(jq -r .authentication.audiences[0] ${ENDPOINTS})
TOKEN=$(curl -s --retry 5 --retry-delay 10 --max-time 60 -f -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=${SPN_CLIENT_ID}" \
--data-urlencode "client_secret=${SPN_CLIENT_SECRET}" \
--data-urlencode "resource=${RESOURCE}" \
${TOKEN_URL} | jq -r '.access_token')
SA_URL="https://management.${FQDN}/${SA_RESOURCE_ID}/listKeys?api-version=2017-10-01"
SA_KEY=$(curl -s --retry 5 --retry-delay 10 --max-time 60 -f -X POST \
"${SA_URL}" -H "Authorization: Bearer ${TOKEN}" -H "Content-Length: 0" | jq -r ".keys[0].value")
}
echo LOCATION: ${LOCATION}
echo TENANT_ID: ${TENANT_ID}
echo ADMIN_USER_NAME: ${ADMIN_USER_NAME}
echo SA_RESOURCE_ID: ${SA_RESOURCE_ID}
echo SA_CONTAINER: ${SA_CONTAINER}
echo KV_RESOURCE_ID: ${KV_RESOURCE_ID}
echo CERT_THUMBPRINT: ${CERT_THUMBPRINT}
echo PIP_FQDN: ${PIP_FQDN}
echo PIP_LABEL: ${PIP_LABEL}
echo REGISTRY_TAG: ${REGISTRY_TAG}
echo REGISTRY_REPLICAS: ${REGISTRY_REPLICAS}
echo SPN_CLIENT_ID: ${SPN_CLIENT_ID}
echo SPN_CLIENT_SECRET: ***
SA_NAME=$(echo ${SA_RESOURCE_ID} | grep -oh -e '[[:alnum:]]*$')
KV_NAME=$(echo ${KV_RESOURCE_ID} | grep -oh -e '[[:alnum:]]*$')
EXT_DOMAIN_NAME="${PIP_FQDN//$PIP_LABEL.$LOCATION.cloudapp.}"
FQDN=${LOCATION}.${EXT_DOMAIN_NAME}
EXTERNAL_FQDN="${FQDN//$PIP_LABEL.$LOCATION.cloudapp.}"
REGISTRY_STORAGE_AZURE_REALM=${LOCATION}.${EXTERNAL_FQDN}
CRT_FILE="${CERT_THUMBPRINT}.crt" CRT_FILE="${CERT_THUMBPRINT}.crt"
KEY_FILE="${CERT_THUMBPRINT}.prv" KEY_FILE="${CERT_THUMBPRINT}.prv"
SECRET=$(openssl rand -base64 32) SECRET=$(openssl rand -base64 32)
if [ -f /var/log.vhd/azure/golden-image-install.complete ]; then if [ -f /opt/azure/vhd-install.complete ]; then
echo "golden image; skipping dependencies installation" echo "aks base image; skipping dependencies installation"
rm -rf /home/packer rm -rf /home/packer
deluser packer deluser packer
groupdel packer groupdel packer
@ -119,8 +200,7 @@ if [ ! -f "/var/lib/waagent/${KEY_FILE}" ]; then
fi fi
echo adding certs to the ca-store echo adding certs to the ca-store
CRT_DST_PATH="/usr/local/share/ca-certificates" cp "/var/lib/waagent/Certificates.pem" "/usr/local/share/ca-certificates/azsCertificate.crt"
cp "/var/lib/waagent/Certificates.pem" "${CRT_DST_PATH}/azsCertificate.crt"
update-ca-certificates update-ca-certificates
echo copy user cert to mount point echo copy user cert to mount point
@ -129,11 +209,17 @@ mkdir -p $STORE
cp "/var/lib/waagent/${CRT_FILE}" "${STORE}/${CRT_FILE}" cp "/var/lib/waagent/${CRT_FILE}" "${STORE}/${CRT_FILE}"
cp "/var/lib/waagent/${KEY_FILE}" "${STORE}/${KEY_FILE}" cp "/var/lib/waagent/${KEY_FILE}" "${STORE}/${KEY_FILE}"
echo moving .htpasswd to mount point echo getting management endpoints
fetchOAuth
echo fetching storage key
fetchStorageKeys
echo fetching user credentials
HTPASSWD_DIR="/root/auth" HTPASSWD_DIR="/root/auth"
mkdir -p $HTPASSWD_DIR mkdir -p $HTPASSWD_DIR
awk '{ sub("\r$", ""); print }' .htpasswd > .htpasswd.tmp fetchCredentials
cp .htpasswd.tmp $HTPASSWD_DIR/.htpasswd cp .htpasswd $HTPASSWD_DIR/.htpasswd
echo starting registry container echo starting registry container
cat <<EOF >> docker-compose.yml cat <<EOF >> docker-compose.yml
@ -153,11 +239,12 @@ services:
- /etc/ssl/certs:/etc/ssl/certs:ro - /etc/ssl/certs:/etc/ssl/certs:ro
- /root/auth:/auth - /root/auth:/auth
environment: environment:
- REGISTRY_LOG_ACCESSLOG_DISABLED=false
- REGISTRY_STORAGE=azure - REGISTRY_STORAGE=azure
- REGISTRY_STORAGE_AZURE_ACCOUNTNAME=${REGISTRY_STORAGE_AZURE_ACCOUNTNAME} - REGISTRY_STORAGE_AZURE_ACCOUNTNAME=${SA_NAME}
- REGISTRY_STORAGE_AZURE_ACCOUNTKEY=${REGISTRY_STORAGE_AZURE_ACCOUNTKEY} - REGISTRY_STORAGE_AZURE_ACCOUNTKEY=${SA_KEY}
- REGISTRY_STORAGE_AZURE_CONTAINER=${REGISTRY_STORAGE_AZURE_CONTAINER} - REGISTRY_STORAGE_AZURE_CONTAINER=${SA_CONTAINER}
- REGISTRY_STORAGE_AZURE_REALM=${REGISTRY_STORAGE_AZURE_REALM} - REGISTRY_STORAGE_AZURE_REALM=${FQDN}
- REGISTRY_AUTH=htpasswd - REGISTRY_AUTH=htpasswd
- REGISTRY_AUTH_HTPASSWD_PATH=/auth/.htpasswd - REGISTRY_AUTH_HTPASSWD_PATH=/auth/.htpasswd
- REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" - REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm"

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

@ -1,24 +1,25 @@
function Random-Name { # Copyright (c) Microsoft Corporation. All rights reserved.
Param ([int]$length) # Licensed under the MIT license.
-join ((97..122) | Get-Random -Count $length | % {[char]$_})
}
# Set variables to match your environment
#########################################
$location = "" $location = ""
$resourceGroup = "" $resourceGroup = ""
$saName = Random-Name 10 $saName = ""
$saContainer = Random-Name 10 $saContainer = ""
$tokenIni = Get-Date
$tokenEnd = $tokenIni.AddYears(1.0)
$kvName = Random-Name 10 $kvName = ""
$secretName = Random-Name 10 $pfxSecret = ""
$pfxPath = "" $pfxPath = ""
$pfxPass = "" $pfxPass = ""
$dnsSubDomain = "" $spnName = ""
$spnSecret = ""
$userName = ""
$userPass = ""
$dnsLabelName = ""
$sshKey = "" $sshKey = ""
$vmSize = ""
$registryTag = "2.7.1"
$registryReplicas = "5"
# RESOURCE GROUP # RESOURCE GROUP
# ============================================= # =============================================
@ -34,25 +35,16 @@ New-AzureRmResourceGroup -Name $resourceGroup -Location $location | out-null
# Create storage account # Create storage account
Write-Host "Creating storage account:" $saName Write-Host "Creating storage account:" $saName
$sa = New-AzureRmStorageAccount -ResourceGroupName $resourceGroup -AccountName $saName -Location $location -SkuName Premium_LRS -EnableHttpsTrafficOnly 1 $sa = New-AzureRmStorageAccount -ResourceGroupName $resourceGroup -AccountName $saName -Location $location -SkuName Premium_LRS -EnableHttpsTrafficOnly 1
$saKey = (Get-AzureRmStorageAccountKey -ResourceGroupName $resourceGroup -AccountName $saName)[0].Value
# Create container # Create container
Write-Host "Creating blob container:" $saContainer Write-Host "Creating blob container:" $saContainer
Set-AzureRmCurrentStorageAccount -ResourceGroupName $resourceGroup -AccountName $saName | out-null Set-AzureRmCurrentStorageAccount -ResourceGroupName $resourceGroup -AccountName $saName | out-null
$container = New-AzureStorageContainer -Name $saContainer New-AzureStorageContainer -Name $saContainer | out-null
# Upload configuration script Write-Host "=> Storage Account Resource ID:" $sa.Id
Write-Host "Uploading configuration script"
Set-AzureStorageBlobContent -Container $saContainer -File script.sh | out-null
$cseToken = New-AzureStorageBlobSASToken -Container $saContainer -Blob "script.sh" -Permission r -StartTime $tokenIni -ExpiryTime $tokenEnd
$cseUrl = $container.CloudBlobContainer.Uri.AbsoluteUri + "/script.sh" + $cseToken
# Upload htpasswd
Write-Host "Uploading htpasswd file"
Set-AzureStorageBlobContent -Container $saContainer -File .htpasswd | out-null
$htpasswdToken = New-AzureStorageBlobSASToken -Container $saContainer -Blob .htpasswd -Permission r -StartTime $tokenIni -ExpiryTime $tokenEnd
$htpasswdUrl = $container.CloudBlobContainer.Uri.AbsoluteUri + "/.htpasswd" + $htpasswdToken
Write-Host "Assigning contributor role to" $spnName
New-AzureRMRoleAssignment -ApplicationId $spnName -RoleDefinitionName "Contributor" -Scope $sa.Id
# KEY VAULT # KEY VAULT
# ============================================= # =============================================
@ -60,8 +52,13 @@ $htpasswdUrl = $container.CloudBlobContainer.Uri.AbsoluteUri + "/.htpasswd" + $h
# Create key vault enabled for deployment # Create key vault enabled for deployment
Write-Host "Creating key vault:" $kvName Write-Host "Creating key vault:" $kvName
$kv = New-AzureRmKeyVault -ResourceGroupName $resourceGroup -VaultName $kvName -Location $location -Sku standard -EnabledForDeployment $kv = New-AzureRmKeyVault -ResourceGroupName $resourceGroup -VaultName $kvName -Location $location -Sku standard -EnabledForDeployment
Write-Host "=> Key Vault Resource ID:" $kv.ResourceId
# Serialize certificate Write-Host "Setting access polices for client" $spnName
Set-AzureRmKeyVaultAccessPolicy -VaultName $kvName -ServicePrincipalName $spnName -PermissionsToSecrets GET,LIST
# Store certificate as secret
Write-Host "Storing certificate in key vault:" $pfxPath
$fileContentBytes = get-content $pfxPath -Encoding Byte $fileContentBytes = get-content $pfxPath -Encoding Byte
$fileContentEncoded = [System.Convert]::ToBase64String($fileContentBytes) $fileContentEncoded = [System.Convert]::ToBase64String($fileContentBytes)
$jsonObject = @" $jsonObject = @"
@ -74,59 +71,75 @@ $jsonObject = @"
$jsonObjectBytes = [System.Text.Encoding]::UTF8.GetBytes($jsonObject) $jsonObjectBytes = [System.Text.Encoding]::UTF8.GetBytes($jsonObject)
$jsonEncoded = [System.Convert]::ToBase64String($jsonObjectBytes) $jsonEncoded = [System.Convert]::ToBase64String($jsonObjectBytes)
$secret = ConvertTo-SecureString -String $jsonEncoded -AsPlainText -Force $secret = ConvertTo-SecureString -String $jsonEncoded -AsPlainText -Force
$kvSecret = Set-AzureKeyVaultSecret -VaultName $kvName -Name $pfxSecret -SecretValue $secret -ContentType pfx
# Upload certificate as secret
Write-Host "Storing certificate in key vault:" $pfxPath
$kvSecret = Set-AzureKeyVaultSecret -VaultName $kvName -Name $secretName -SecretValue $secret
# Compute certificate thumbprint # Compute certificate thumbprint
Write-Host "Computing certificate thumbprint" Write-Host "Computing certificate thumbprint"
$tp = Get-PfxCertificate -FilePath $pfxPath $tp = Get-PfxCertificate -FilePath $pfxPath
Write-Host "=> Certificate URL:" $kvSecret.Id
Write-Host "=> Certificate thumbprint:" $tp.Thumbprint
Write-Host "Storing secret for sample user: $userName"
$userSecret = ConvertTo-SecureString -String $userPass -AsPlainText -Force
Set-AzureKeyVaultSecret -VaultName $kvName -Name $userName -SecretValue $userSecret -ContentType "user credentials" | out-null
# BUILD TEMPLATE PARAMETERS JSON # BUILD TEMPLATE PARAMETERS JSON
# ============================================= # =============================================
$jsonParameters = New-Object -TypeName PSObject $jsonParameters = New-Object -TypeName PSObject
$jsonStorageAccountName = New-Object -TypeName PSObject $jsonAdminPublicKey = New-Object -TypeName PSObject
$jsonStorageAccountName | Add-Member -MemberType NoteProperty -Name value -Value $saName $jsonAdminPublicKey | Add-Member -MemberType NoteProperty -Name value -Value $sshKey
$jsonParameters | Add-Member -MemberType NoteProperty -Name storageAccountName -Value $jsonStorageAccountName $jsonParameters | Add-Member -MemberType NoteProperty -Name adminPublicKey -Value $jsonAdminPublicKey
$jsonVirtualMachineSize = New-Object -TypeName PSObject
$jsonVirtualMachineSize | Add-Member -MemberType NoteProperty -Name value -Value $vmSize
$jsonParameters | Add-Member -MemberType NoteProperty -Name virtualMachineSize -Value $jsonVirtualMachineSize
$jsonPipName = New-Object -TypeName PSObject
$jsonPipName | Add-Member -MemberType NoteProperty -Name value -Value $dnsLabelName
$jsonParameters | Add-Member -MemberType NoteProperty -Name pipName -Value $jsonPipName
$jsonPipDomainNameLabel = New-Object -TypeName PSObject
$jsonPipDomainNameLabel | Add-Member -MemberType NoteProperty -Name value -Value $dnsLabelName
$jsonParameters | Add-Member -MemberType NoteProperty -Name pipDomainNameLabel -Value $jsonPipDomainNameLabel
$jsonStorageAccountResourceId = New-Object -TypeName PSObject
$jsonStorageAccountResourceId | Add-Member -MemberType NoteProperty -Name value -Value $sa.Id
$jsonParameters | Add-Member -MemberType NoteProperty -Name storageAccountResourceId -Value $jsonStorageAccountResourceId
$jsonStorageAccountContainerName = New-Object -TypeName PSObject $jsonStorageAccountContainerName = New-Object -TypeName PSObject
$jsonStorageAccountContainerName | Add-Member -MemberType NoteProperty -Name value -Value $saContainer $jsonStorageAccountContainerName | Add-Member -MemberType NoteProperty -Name value -Value $saContainer
$jsonParameters | Add-Member -MemberType NoteProperty -Name storageAccountContainer -Value $jsonStorageAccountContainerName $jsonParameters | Add-Member -MemberType NoteProperty -Name storageAccountContainer -Value $jsonStorageAccountContainerName
$jsonStorageAccountKey = New-Object -TypeName PSObject
$jsonStorageAccountKey | Add-Member -MemberType NoteProperty -Name value -Value $saKey
$jsonParameters | Add-Member -MemberType NoteProperty -Name storageAccountKey -Value $jsonStorageAccountKey
$jsonKeyVaultResourceId = New-Object -TypeName PSObject $jsonKeyVaultResourceId = New-Object -TypeName PSObject
$jsonKeyVaultResourceId | Add-Member -MemberType NoteProperty -Name value -Value $kv.ResourceId $jsonKeyVaultResourceId | Add-Member -MemberType NoteProperty -Name value -Value $kv.ResourceId
$jsonParameters | Add-Member -MemberType NoteProperty -Name keyVaultResourceId -Value $jsonKeyVaultResourceId $jsonParameters | Add-Member -MemberType NoteProperty -Name pfxKeyVaultResourceId -Value $jsonKeyVaultResourceId
$jsonKeyVaultSecretUrl = New-Object -TypeName PSObject $jsonKeyVaultSecretUrl = New-Object -TypeName PSObject
$jsonKeyVaultSecretUrl | Add-Member -MemberType NoteProperty -Name value -Value $kvSecret.Id $jsonKeyVaultSecretUrl | Add-Member -MemberType NoteProperty -Name value -Value $kvSecret.Id
$jsonParameters | Add-Member -MemberType NoteProperty -Name keyVaultSecretUrl -Value $jsonKeyVaultSecretUrl $jsonParameters | Add-Member -MemberType NoteProperty -Name pfxKeyVaultSecretUrl -Value $jsonKeyVaultSecretUrl
$jsonCertificateThumbprint = New-Object -TypeName PSObject $jsonCertificateThumbprint = New-Object -TypeName PSObject
$jsonCertificateThumbprint | Add-Member -MemberType NoteProperty -Name value -Value $tp.Thumbprint $jsonCertificateThumbprint | Add-Member -MemberType NoteProperty -Name value -Value $tp.Thumbprint
$jsonParameters | Add-Member -MemberType NoteProperty -Name certificateThumbprint -Value $jsonCertificateThumbprint $jsonParameters | Add-Member -MemberType NoteProperty -Name pfxThumbprint -Value $jsonCertificateThumbprint
$jsonAdminPublicKey = New-Object -TypeName PSObject $jsonRegistryTag = New-Object -TypeName PSObject
$jsonAdminPublicKey | Add-Member -MemberType NoteProperty -Name value -Value $sshKey $jsonRegistryTag | Add-Member -MemberType NoteProperty -Name value -Value $registryTag
$jsonParameters | Add-Member -MemberType NoteProperty -Name adminPublicKey -Value $jsonAdminPublicKey $jsonParameters | Add-Member -MemberType NoteProperty -Name registryTag -Value $jsonRegistryTag
$jsonDomainNameLabel = New-Object -TypeName PSObject $jsonRegistryReplicas = New-Object -TypeName PSObject
$jsonDomainNameLabel | Add-Member -MemberType NoteProperty -Name value -Value $dnsSubDomain $jsonRegistryReplicas | Add-Member -MemberType NoteProperty -Name value -Value $registryReplicas
$jsonParameters | Add-Member -MemberType NoteProperty -Name domainNameLabel -Value $jsonDomainNameLabel $jsonParameters | Add-Member -MemberType NoteProperty -Name registryReplicas -Value $jsonRegistryReplicas
$jsonCseLocation = New-Object -TypeName PSObject $jsonSpnName = New-Object -TypeName PSObject
$jsonCseLocation | Add-Member -MemberType NoteProperty -Name value -Value $cseUrl $jsonSpnName | Add-Member -MemberType NoteProperty -Name value -Value $spnName
$jsonParameters | Add-Member -MemberType NoteProperty -Name cseLocation -Value $jsonCseLocation $jsonParameters | Add-Member -MemberType NoteProperty -Name servicePrincipalClientId -Value $jsonSpnName
$jsonHtpasswdLocation = New-Object -TypeName PSObject $jsonSpnSecret = New-Object -TypeName PSObject
$jsonHtpasswdLocation | Add-Member -MemberType NoteProperty -Name value -Value $htpasswdUrl $jsonSpnSecret | Add-Member -MemberType NoteProperty -Name value -Value $spnSecret
$jsonParameters | Add-Member -MemberType NoteProperty -Name htpasswdLocation -Value $jsonHtpasswdLocation $jsonParameters | Add-Member -MemberType NoteProperty -Name servicePrincipalClientSecret -Value $jsonSpnSecret
$jsonRoot = New-Object -TypeName PSObject $jsonRoot = New-Object -TypeName PSObject
$jsonRoot | Add-Member -MemberType NoteProperty -Name schema -Value "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#" $jsonRoot | Add-Member -MemberType NoteProperty -Name schema -Value "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#"