├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── SECURITY.md ├── cloud-init.txt ├── config.toml ├── edgeDeploy.bicep ├── edgeDeploy.json └── genoneline.py /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Microsoft Azure 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iotedge-vm-deploy 2 | 3 | Detailed documentation is available on [Microsoft Docs](https://docs.microsoft.com/en-us/azure/iot-edge/how-to-install-iot-edge-ubuntuvm) 4 | 5 | ## ARM Template to deploy IoT Edge enabled VM 6 | 7 | The following Azure Resource Templates are for IoT Edge release 1.5. 8 | 9 | ARM template to deploy a VM with IoT Edge pre-installed (via cloud-init) 10 | 11 | 12 | 13 | 14 | 15 | The ARM template visualized for exploration 16 | 17 | 18 | 19 | 20 | ## Azure CLI command to deploy IoT Edge enabled VM 21 | 22 | The commands below deploy a VM with IoT Edge pre-installed **and provisioned** with the provided connection string. To deploy a VM with IoT Edge pre-installed but **not provisioned**, do not include the `--parameters deviceConnectionString=...` portion of the command. 23 | 24 | ### Using an ARM template 25 | 26 | ```bash 27 | az deployment group create \ 28 | --name edgeVm \ 29 | --resource-group replace-with-rg-name \ 30 | --template-uri "https://raw.githubusercontent.com/Azure/iotedge-vm-deploy/main/edgeDeploy.json" \ 31 | --parameters dnsLabelPrefix='my-edge-vm1' \ 32 | --parameters adminUsername='azureuser' \ 33 | --parameters authenticationType='sshPublicKey' \ 34 | --parameters adminPasswordOrKey="$(< ~/.ssh/id_rsa.pub)" \ 35 | --parameters deviceConnectionString=$(az iot hub device-identity show-connection-string --device-id replace-with-device-name --hub-name replace-with-hub-name -o tsv) 36 | ``` 37 | 38 | ### Using a local Azure Bicep file 39 | 40 | ```bash 41 | az deployment group create \ 42 | --name edgeVm \ 43 | --resource-group replace-with-rg-name \ 44 | --template-file "./edgeDeploy.bicep" \ 45 | --parameters dnsLabelPrefix='my-edge-vm1' \ 46 | --parameters adminUsername='azureuser' \ 47 | --parameters authenticationType='sshPublicKey' \ 48 | --parameters adminPasswordOrKey="$(< ~/.ssh/id_rsa.pub)" \ 49 | --parameters deviceConnectionString=$(az iot hub device-identity show-connection-string --device-id replace-with-device-name --hub-name replace-with-hub-name -o tsv) 50 | ``` 51 | 52 | # Contributing 53 | 54 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 55 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 56 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 57 | 58 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 59 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 60 | provided by the bot. You will only need to do this once across all repos using our CLA. 61 | 62 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 63 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 64 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 65 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /cloud-init.txt: -------------------------------------------------------------------------------- 1 | #cloud-config 2 | 3 | apt: 4 | preserve_sources_list: true 5 | sources: 6 | msft.list: 7 | source: "deb https://packages.microsoft.com/ubuntu/22.04/prod jammy main" 8 | key: | 9 | -----BEGIN PGP PUBLIC KEY BLOCK----- 10 | Version: GnuPG v1.4.7 (GNU/Linux) 11 | 12 | mQENBFYxWIwBCADAKoZhZlJxGNGWzqV+1OG1xiQeoowKhssGAKvd+buXCGISZJwT 13 | LXZqIcIiLP7pqdcZWtE9bSc7yBY2MalDp9Liu0KekywQ6VVX1T72NPf5Ev6x6DLV 14 | 7aVWsCzUAF+eb7DC9fPuFLEdxmOEYoPjzrQ7cCnSV4JQxAqhU4T6OjbvRazGl3ag 15 | OeizPXmRljMtUUttHQZnRhtlzkmwIrUivbfFPD+fEoHJ1+uIdfOzZX8/oKHKLe2j 16 | H632kvsNzJFlROVvGLYAk2WRcLu+RjjggixhwiB+Mu/A8Tf4V6b+YppS44q8EvVr 17 | M+QvY7LNSOffSO6Slsy9oisGTdfE39nC7pVRABEBAAG0N01pY3Jvc29mdCAoUmVs 18 | ZWFzZSBzaWduaW5nKSA8Z3Bnc2VjdXJpdHlAbWljcm9zb2Z0LmNvbT6JATUEEwEC 19 | AB8FAlYxWIwCGwMGCwkIBwMCBBUCCAMDFgIBAh4BAheAAAoJEOs+lK2+EinPGpsH 20 | /32vKy29Hg51H9dfFJMx0/a/F+5vKeCeVqimvyTM04C+XENNuSbYZ3eRPHGHFLqe 21 | MNGxsfb7C7ZxEeW7J/vSzRgHxm7ZvESisUYRFq2sgkJ+HFERNrqfci45bdhmrUsy 22 | 7SWw9ybxdFOkuQoyKD3tBmiGfONQMlBaOMWdAsic965rvJsd5zYaZZFI1UwTkFXV 23 | KJt3bp3Ngn1vEYXwijGTa+FXz6GLHueJwF0I7ug34DgUkAFvAs8Hacr2DRYxL5RJ 24 | XdNgj4Jd2/g6T9InmWT0hASljur+dJnzNiNCkbn9KbX7J/qK1IbR8y560yRmFsU+ 25 | NdCFTW7wY0Fb1fWJ+/KTsC4= 26 | =J6gs 27 | -----END PGP PUBLIC KEY BLOCK----- 28 | packages: 29 | - moby-cli 30 | - moby-engine 31 | runcmd: 32 | - dcs="{{{dcs}}}" 33 | - | 34 | set -x 35 | ( 36 | 37 | # Wait for docker daemon to start 38 | while [ $(ps -ef | grep -v grep | grep docker | wc -l) -le 0 ]; do 39 | sleep 3 40 | done 41 | 42 | apt install -y aziot-edge 43 | 44 | if [ ! -z $dcs ]; then 45 | mkdir /etc/aziot 46 | wget https://raw.githubusercontent.com/Azure/iotedge-vm-deploy/main/config.toml -O /etc/aziot/config.toml 47 | sed -i "s#\(connection_string = \).*#\1\"$dcs\"#g" /etc/aziot/config.toml 48 | iotedge config apply -c /etc/aziot/config.toml 49 | fi 50 | 51 | ) & 52 | -------------------------------------------------------------------------------- /config.toml: -------------------------------------------------------------------------------- 1 | [provisioning] 2 | source = "manual" 3 | connection_string = 4 | 5 | [agent] 6 | name = "edgeAgent" 7 | type = "docker" 8 | 9 | [agent.config] 10 | image = "mcr.microsoft.com/azureiotedge-agent:1.5" 11 | 12 | [connect] 13 | workload_uri = "unix:///var/run/iotedge/workload.sock" 14 | management_uri = "unix:///var/run/iotedge/mgmt.sock" 15 | 16 | [listen] 17 | workload_uri = "fd://aziot-edged.workload.socket" 18 | management_uri = "fd://aziot-edged.mgmt.socket" 19 | 20 | [moby_runtime] 21 | uri = "unix:///var/run/docker.sock" 22 | network = "azure-iot-edge" 23 | -------------------------------------------------------------------------------- /edgeDeploy.bicep: -------------------------------------------------------------------------------- 1 | @description('The location of the resources.') 2 | param location string = resourceGroup().location 3 | 4 | @description('Unique DNS Name for the Storage Account where the Virtual Machine\'s disks will be placed.') 5 | param dnsLabelPrefix string 6 | 7 | @description('User name for the Virtual Machine.') 8 | param adminUsername string 9 | 10 | @description('SSH Key or password for the Virtual Machine. SSH key is recommended.') 11 | @secure() 12 | param adminPasswordOrKey string 13 | 14 | @description('Type of authentication to use on the Virtual Machine. SSH key is recommended.') 15 | @allowed([ 16 | 'sshPublicKey' 17 | 'password' 18 | ]) 19 | param authenticationType string = 'sshPublicKey' 20 | 21 | @description('VM size') 22 | param vmSize string = 'Standard_DS1_v2' 23 | 24 | @description('The Ubuntu version for the VM. This will pick a fully patched image of this given Ubuntu version.') 25 | param ubuntuOSVersion string = '22_04-lts' 26 | 27 | @description('IoT Edge Device Connection String') 28 | @secure() 29 | param deviceConnectionString string 30 | 31 | @description('Allow SSH traffic through the firewall') 32 | param allowSsh bool = true 33 | 34 | var imagePublisher = 'Canonical' 35 | var imageOffer = '0001-com-ubuntu-server-jammy' 36 | var nicName = 'nic-${uniqueString(dnsLabelPrefix)}' 37 | var vmName = 'vm-${uniqueString(dnsLabelPrefix)}' 38 | var vnetName = 'vnet-${uniqueString(dnsLabelPrefix)}' 39 | var pipName = 'ip-${dnsLabelPrefix}' 40 | var addressPrefix = '10.0.0.0/16' 41 | var subnet1Name = 'subnet-${uniqueString(dnsLabelPrefix)}' 42 | var subnet1Prefix = '10.0.0.0/24' 43 | var publicIPAddressType = 'Dynamic' 44 | var vnetID = vnet.id 45 | var subnet1Ref = '${vnetID}/subnets/${subnet1Name}' 46 | var linuxConfiguration = { 47 | disablePasswordAuthentication: true 48 | ssh: { 49 | publicKeys: [ 50 | { 51 | path: '/home/${adminUsername}/.ssh/authorized_keys' 52 | keyData: adminPasswordOrKey 53 | } 54 | ] 55 | } 56 | } 57 | var dcs = deviceConnectionString 58 | var networkSecurityGroupName_var = 'nsg-${uniqueString(dnsLabelPrefix)}' 59 | var sshRule = [ 60 | { 61 | name: 'default-allow-22' 62 | properties: { 63 | priority: 1000 64 | access: 'Allow' 65 | direction: 'Inbound' 66 | destinationPortRange: '22' 67 | protocol: 'Tcp' 68 | sourceAddressPrefix: '*' 69 | sourcePortRange: '*' 70 | destinationAddressPrefix: '*' 71 | } 72 | } 73 | ] 74 | var noRule = [] 75 | 76 | resource pip 'Microsoft.Network/publicIPAddresses@2021-08-01' = { 77 | name: pipName 78 | location: location 79 | properties: { 80 | publicIPAllocationMethod: publicIPAddressType 81 | dnsSettings: { 82 | domainNameLabel: dnsLabelPrefix 83 | } 84 | } 85 | } 86 | 87 | resource nsg 'Microsoft.Network/networkSecurityGroups@2021-08-01' = { 88 | name: networkSecurityGroupName_var 89 | location: location 90 | properties: { 91 | securityRules: (allowSsh ? sshRule : noRule) 92 | } 93 | } 94 | 95 | resource vnet 'Microsoft.Network/virtualNetworks@2021-08-01' = { 96 | name: vnetName 97 | location: location 98 | properties: { 99 | addressSpace: { 100 | addressPrefixes: [ 101 | addressPrefix 102 | ] 103 | } 104 | subnets: [ 105 | { 106 | name: subnet1Name 107 | properties: { 108 | addressPrefix: subnet1Prefix 109 | networkSecurityGroup: { 110 | id: nsg.id 111 | } 112 | } 113 | } 114 | ] 115 | } 116 | } 117 | 118 | resource nic 'Microsoft.Network/networkInterfaces@2021-08-01' = { 119 | name: nicName 120 | location: location 121 | properties: { 122 | ipConfigurations: [ 123 | { 124 | name: 'ipconfig1' 125 | properties: { 126 | privateIPAllocationMethod: 'Dynamic' 127 | publicIPAddress: { 128 | id: pip.id 129 | } 130 | subnet: { 131 | id: subnet1Ref 132 | } 133 | } 134 | } 135 | ] 136 | } 137 | } 138 | 139 | resource vm 'Microsoft.Compute/virtualMachines@2022-03-01' = { 140 | name: vmName 141 | location: location 142 | properties: { 143 | hardwareProfile: { 144 | vmSize: vmSize 145 | } 146 | osProfile: { 147 | computerName: vmName 148 | adminUsername: adminUsername 149 | adminPassword: adminPasswordOrKey 150 | customData: base64('#cloud-config\n\napt:\n preserve_sources_list: true\n sources:\n msft.list:\n source: "deb https://packages.microsoft.com/ubuntu/22.04/prod jammy main"\n key: |\n -----BEGIN PGP PUBLIC KEY BLOCK-----\n Version: GnuPG v1.4.7 (GNU/Linux)\n\n mQENBFYxWIwBCADAKoZhZlJxGNGWzqV+1OG1xiQeoowKhssGAKvd+buXCGISZJwT\n LXZqIcIiLP7pqdcZWtE9bSc7yBY2MalDp9Liu0KekywQ6VVX1T72NPf5Ev6x6DLV\n 7aVWsCzUAF+eb7DC9fPuFLEdxmOEYoPjzrQ7cCnSV4JQxAqhU4T6OjbvRazGl3ag\n OeizPXmRljMtUUttHQZnRhtlzkmwIrUivbfFPD+fEoHJ1+uIdfOzZX8/oKHKLe2j\n H632kvsNzJFlROVvGLYAk2WRcLu+RjjggixhwiB+Mu/A8Tf4V6b+YppS44q8EvVr\n M+QvY7LNSOffSO6Slsy9oisGTdfE39nC7pVRABEBAAG0N01pY3Jvc29mdCAoUmVs\n ZWFzZSBzaWduaW5nKSA8Z3Bnc2VjdXJpdHlAbWljcm9zb2Z0LmNvbT6JATUEEwEC\n AB8FAlYxWIwCGwMGCwkIBwMCBBUCCAMDFgIBAh4BAheAAAoJEOs+lK2+EinPGpsH\n /32vKy29Hg51H9dfFJMx0/a/F+5vKeCeVqimvyTM04C+XENNuSbYZ3eRPHGHFLqe\n MNGxsfb7C7ZxEeW7J/vSzRgHxm7ZvESisUYRFq2sgkJ+HFERNrqfci45bdhmrUsy\n 7SWw9ybxdFOkuQoyKD3tBmiGfONQMlBaOMWdAsic965rvJsd5zYaZZFI1UwTkFXV\n KJt3bp3Ngn1vEYXwijGTa+FXz6GLHueJwF0I7ug34DgUkAFvAs8Hacr2DRYxL5RJ\n XdNgj4Jd2/g6T9InmWT0hASljur+dJnzNiNCkbn9KbX7J/qK1IbR8y560yRmFsU+\n NdCFTW7wY0Fb1fWJ+/KTsC4=\n =J6gs\n -----END PGP PUBLIC KEY BLOCK----- \npackages:\n - moby-cli\n - moby-engine\nruncmd:\n - dcs="${dcs}"\n - |\n set -x\n (\n\n # Wait for docker daemon to start\n while [ $(ps -ef | grep -v grep | grep docker | wc -l) -le 0 ]; do \n sleep 3\n done\n\n apt install -y aziot-edge\n\n if [ ! -z $dcs ]; then\n mkdir /etc/aziot\n wget https://raw.githubusercontent.com/Azure/iotedge-vm-deploy/main/config.toml -O /etc/aziot/config.toml\n sed -i "s#\\(connection_string = \\).*#\\1\\"$dcs\\"#g" /etc/aziot/config.toml\n iotedge config apply -c /etc/aziot/config.toml\n fi\n\n ) &\n') 151 | linuxConfiguration: ((authenticationType == 'password') ? json('null') : linuxConfiguration) 152 | } 153 | storageProfile: { 154 | imageReference: { 155 | publisher: imagePublisher 156 | offer: imageOffer 157 | sku: ubuntuOSVersion 158 | version: 'latest' 159 | } 160 | osDisk: { 161 | createOption: 'FromImage' 162 | } 163 | } 164 | networkProfile: { 165 | networkInterfaces: [ 166 | { 167 | id: nic.id 168 | } 169 | ] 170 | } 171 | } 172 | } 173 | 174 | output Public_SSH string = 'ssh ${vm.properties.osProfile.adminUsername}@${pip.properties.dnsSettings.fqdn}' 175 | -------------------------------------------------------------------------------- /edgeDeploy.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "metadata": { 5 | "_generator": { 6 | "name": "bicep", 7 | "version": "0.9.1.41621", 8 | "templateHash": "956004028010656602" 9 | } 10 | }, 11 | "parameters": { 12 | "location": { 13 | "type": "string", 14 | "defaultValue": "[resourceGroup().location]", 15 | "metadata": { 16 | "description": "The location of the resources." 17 | } 18 | }, 19 | "dnsLabelPrefix": { 20 | "type": "string", 21 | "metadata": { 22 | "description": "Unique DNS Name for the Storage Account where the Virtual Machine's disks will be placed." 23 | } 24 | }, 25 | "adminUsername": { 26 | "type": "string", 27 | "metadata": { 28 | "description": "User name for the Virtual Machine." 29 | } 30 | }, 31 | "adminPasswordOrKey": { 32 | "type": "securestring", 33 | "metadata": { 34 | "description": "SSH Key or password for the Virtual Machine. SSH key is recommended." 35 | } 36 | }, 37 | "authenticationType": { 38 | "type": "string", 39 | "defaultValue": "sshPublicKey", 40 | "allowedValues": [ 41 | "sshPublicKey", 42 | "password" 43 | ], 44 | "metadata": { 45 | "description": "Type of authentication to use on the Virtual Machine. SSH key is recommended." 46 | } 47 | }, 48 | "vmSize": { 49 | "type": "string", 50 | "defaultValue": "Standard_DS1_v2", 51 | "metadata": { 52 | "description": "VM size" 53 | } 54 | }, 55 | "ubuntuOSVersion": { 56 | "type": "string", 57 | "defaultValue": "22_04-lts", 58 | "metadata": { 59 | "description": "The Ubuntu version for the VM. This will pick a fully patched image of this given Ubuntu version." 60 | } 61 | }, 62 | "deviceConnectionString": { 63 | "type": "securestring", 64 | "metadata": { 65 | "description": "IoT Edge Device Connection String" 66 | } 67 | }, 68 | "allowSsh": { 69 | "type": "bool", 70 | "defaultValue": true, 71 | "metadata": { 72 | "description": "Allow SSH traffic through the firewall" 73 | } 74 | } 75 | }, 76 | "variables": { 77 | "imagePublisher": "Canonical", 78 | "imageOffer": "0001-com-ubuntu-server-jammy", 79 | "nicName": "[format('nic-{0}', uniqueString(parameters('dnsLabelPrefix')))]", 80 | "vmName": "[format('vm-{0}', uniqueString(parameters('dnsLabelPrefix')))]", 81 | "vnetName": "[format('vnet-{0}', uniqueString(parameters('dnsLabelPrefix')))]", 82 | "pipName": "[format('ip-{0}', parameters('dnsLabelPrefix'))]", 83 | "addressPrefix": "10.0.0.0/16", 84 | "subnet1Name": "[format('subnet-{0}', uniqueString(parameters('dnsLabelPrefix')))]", 85 | "subnet1Prefix": "10.0.0.0/24", 86 | "publicIPAddressType": "Dynamic", 87 | "vnetID": "[resourceId('Microsoft.Network/virtualNetworks', variables('vnetName'))]", 88 | "subnet1Ref": "[format('{0}/subnets/{1}', variables('vnetID'), variables('subnet1Name'))]", 89 | "linuxConfiguration": { 90 | "disablePasswordAuthentication": true, 91 | "ssh": { 92 | "publicKeys": [ 93 | { 94 | "path": "[format('/home/{0}/.ssh/authorized_keys', parameters('adminUsername'))]", 95 | "keyData": "[parameters('adminPasswordOrKey')]" 96 | } 97 | ] 98 | } 99 | }, 100 | "dcs": "[parameters('deviceConnectionString')]", 101 | "networkSecurityGroupName_var": "[format('nsg-{0}', uniqueString(parameters('dnsLabelPrefix')))]", 102 | "sshRule": [ 103 | { 104 | "name": "default-allow-22", 105 | "properties": { 106 | "priority": 1000, 107 | "access": "Allow", 108 | "direction": "Inbound", 109 | "destinationPortRange": "22", 110 | "protocol": "Tcp", 111 | "sourceAddressPrefix": "*", 112 | "sourcePortRange": "*", 113 | "destinationAddressPrefix": "*" 114 | } 115 | } 116 | ], 117 | "noRule": [] 118 | }, 119 | "resources": [ 120 | { 121 | "type": "Microsoft.Network/publicIPAddresses", 122 | "apiVersion": "2021-08-01", 123 | "name": "[variables('pipName')]", 124 | "location": "[parameters('location')]", 125 | "properties": { 126 | "publicIPAllocationMethod": "[variables('publicIPAddressType')]", 127 | "dnsSettings": { 128 | "domainNameLabel": "[parameters('dnsLabelPrefix')]" 129 | } 130 | } 131 | }, 132 | { 133 | "type": "Microsoft.Network/networkSecurityGroups", 134 | "apiVersion": "2021-08-01", 135 | "name": "[variables('networkSecurityGroupName_var')]", 136 | "location": "[parameters('location')]", 137 | "properties": { 138 | "securityRules": "[if(parameters('allowSsh'), variables('sshRule'), variables('noRule'))]" 139 | } 140 | }, 141 | { 142 | "type": "Microsoft.Network/virtualNetworks", 143 | "apiVersion": "2021-08-01", 144 | "name": "[variables('vnetName')]", 145 | "location": "[parameters('location')]", 146 | "properties": { 147 | "addressSpace": { 148 | "addressPrefixes": [ 149 | "[variables('addressPrefix')]" 150 | ] 151 | }, 152 | "subnets": [ 153 | { 154 | "name": "[variables('subnet1Name')]", 155 | "properties": { 156 | "addressPrefix": "[variables('subnet1Prefix')]", 157 | "networkSecurityGroup": { 158 | "id": "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName_var'))]" 159 | } 160 | } 161 | } 162 | ] 163 | }, 164 | "dependsOn": [ 165 | "[resourceId('Microsoft.Network/networkSecurityGroups', variables('networkSecurityGroupName_var'))]" 166 | ] 167 | }, 168 | { 169 | "type": "Microsoft.Network/networkInterfaces", 170 | "apiVersion": "2021-08-01", 171 | "name": "[variables('nicName')]", 172 | "location": "[parameters('location')]", 173 | "properties": { 174 | "ipConfigurations": [ 175 | { 176 | "name": "ipconfig1", 177 | "properties": { 178 | "privateIPAllocationMethod": "Dynamic", 179 | "publicIPAddress": { 180 | "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('pipName'))]" 181 | }, 182 | "subnet": { 183 | "id": "[variables('subnet1Ref')]" 184 | } 185 | } 186 | } 187 | ] 188 | }, 189 | "dependsOn": [ 190 | "[resourceId('Microsoft.Network/publicIPAddresses', variables('pipName'))]", 191 | "[resourceId('Microsoft.Network/virtualNetworks', variables('vnetName'))]" 192 | ] 193 | }, 194 | { 195 | "type": "Microsoft.Compute/virtualMachines", 196 | "apiVersion": "2022-03-01", 197 | "name": "[variables('vmName')]", 198 | "location": "[parameters('location')]", 199 | "properties": { 200 | "hardwareProfile": { 201 | "vmSize": "[parameters('vmSize')]" 202 | }, 203 | "osProfile": { 204 | "computerName": "[variables('vmName')]", 205 | "adminUsername": "[parameters('adminUsername')]", 206 | "adminPassword": "[parameters('adminPasswordOrKey')]", 207 | "customData": "[base64(concat('#cloud-config\n\napt:\n preserve_sources_list: true\n sources:\n msft.list:\n source: \"deb https://packages.microsoft.com/ubuntu/22.04/prod jammy main\"\n key: |\n -----BEGIN PGP PUBLIC KEY BLOCK-----\n Version: GnuPG v1.4.7 (GNU/Linux)\n\n mQENBFYxWIwBCADAKoZhZlJxGNGWzqV+1OG1xiQeoowKhssGAKvd+buXCGISZJwT\n LXZqIcIiLP7pqdcZWtE9bSc7yBY2MalDp9Liu0KekywQ6VVX1T72NPf5Ev6x6DLV\n 7aVWsCzUAF+eb7DC9fPuFLEdxmOEYoPjzrQ7cCnSV4JQxAqhU4T6OjbvRazGl3ag\n OeizPXmRljMtUUttHQZnRhtlzkmwIrUivbfFPD+fEoHJ1+uIdfOzZX8/oKHKLe2j\n H632kvsNzJFlROVvGLYAk2WRcLu+RjjggixhwiB+Mu/A8Tf4V6b+YppS44q8EvVr\n M+QvY7LNSOffSO6Slsy9oisGTdfE39nC7pVRABEBAAG0N01pY3Jvc29mdCAoUmVs\n ZWFzZSBzaWduaW5nKSA8Z3Bnc2VjdXJpdHlAbWljcm9zb2Z0LmNvbT6JATUEEwEC\n AB8FAlYxWIwCGwMGCwkIBwMCBBUCCAMDFgIBAh4BAheAAAoJEOs+lK2+EinPGpsH\n /32vKy29Hg51H9dfFJMx0/a/F+5vKeCeVqimvyTM04C+XENNuSbYZ3eRPHGHFLqe\n MNGxsfb7C7ZxEeW7J/vSzRgHxm7ZvESisUYRFq2sgkJ+HFERNrqfci45bdhmrUsy\n 7SWw9ybxdFOkuQoyKD3tBmiGfONQMlBaOMWdAsic965rvJsd5zYaZZFI1UwTkFXV\n KJt3bp3Ngn1vEYXwijGTa+FXz6GLHueJwF0I7ug34DgUkAFvAs8Hacr2DRYxL5RJ\n XdNgj4Jd2/g6T9InmWT0hASljur+dJnzNiNCkbn9KbX7J/qK1IbR8y560yRmFsU+\n NdCFTW7wY0Fb1fWJ+/KTsC4=\n =J6gs\n -----END PGP PUBLIC KEY BLOCK----- \npackages:\n - moby-cli\n - moby-engine\nruncmd:\n - dcs=\"',variables('dcs'),'\"\n - |\n set -x\n (\n\n # Wait for docker daemon to start\n while [ $(ps -ef | grep -v grep | grep docker | wc -l) -le 0 ]; do \n sleep 3\n done\n\n apt install -y aziot-edge\n\n if [ ! -z $dcs ]; then\n mkdir /etc/aziot\n wget https://raw.githubusercontent.com/Azure/iotedge-vm-deploy/main/config.toml -O /etc/aziot/config.toml\n sed -i \"s#\\(connection_string = \\).*#\\1\\\"$dcs\\\"#g\" /etc/aziot/config.toml\n iotedge config apply -c /etc/aziot/config.toml\n fi\n\n ) &\n'))]", 208 | "linuxConfiguration": "[if(equals(parameters('authenticationType'), 'password'), json('null'), variables('linuxConfiguration'))]" 209 | }, 210 | "storageProfile": { 211 | "imageReference": { 212 | "publisher": "[variables('imagePublisher')]", 213 | "offer": "[variables('imageOffer')]", 214 | "sku": "[parameters('ubuntuOSVersion')]", 215 | "version": "latest" 216 | }, 217 | "osDisk": { 218 | "createOption": "FromImage" 219 | } 220 | }, 221 | "networkProfile": { 222 | "networkInterfaces": [ 223 | { 224 | "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" 225 | } 226 | ] 227 | } 228 | }, 229 | "dependsOn": [ 230 | "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" 231 | ] 232 | } 233 | ], 234 | "outputs": { 235 | "Public_SSH": { 236 | "type": "string", 237 | "value": "[format('ssh {0}@{1}', reference(resourceId('Microsoft.Compute/virtualMachines', variables('vmName'))).osProfile.adminUsername, reference(resourceId('Microsoft.Network/publicIPAddresses', variables('pipName'))).dnsSettings.fqdn)]" 238 | } 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /genoneline.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | '''genonline.py - Python 3 version of AnHowe's script to package a cloud-init.txt script into an 3 | Azure Resource Manage template format. 4 | goal: "commandToExecute": "[variables('jumpboxWindowsCustomScript')] 5 | "''' 6 | import os 7 | import re 8 | import sys 9 | 10 | 11 | def convertToOneArmTemplateLine(file): 12 | with open(file) as f: 13 | content = f.read() 14 | 15 | # convert to one line 16 | content = content.replace("\\", "\\\\") 17 | content = content.replace("\r\n", "\\n") 18 | content = content.replace("\n", "\\n") 19 | content = content.replace('"', '\\"') 20 | 21 | # replace {{{ }}} with variable names 22 | return re.sub(r"{{{([^}]*)}}}", r"',variables('\1'),'", content) 23 | 24 | def convertToOneAzureBicepLine(file): 25 | with open(file) as f: 26 | content = f.read() 27 | 28 | # convert to one line 29 | content = content.replace("\\", "\\\\") 30 | content = content.replace("\r\n", "\\n") 31 | content = content.replace("\n", "\\n") 32 | content = content.replace("\t", "\\t") 33 | content = content.replace("'", "\\'") 34 | 35 | # replace {{{ }}} with variable names 36 | return re.sub(r"{{{([^}]*)}}}", r"${\1}", content) 37 | 38 | 39 | def usage(): 40 | print(' usage: ', os.path.basename(sys.argv[0]), 'file1') 41 | print(' builds a one line string to send to commandToExecute') 42 | 43 | 44 | def main(): 45 | if len(sys.argv) != 2: 46 | usage() 47 | sys.exit(1) 48 | 49 | file = sys.argv[1] 50 | if not os.path.exists(file): 51 | sys.exit('Error: file: ' + file + ' does not exist') 52 | 53 | # build the yml file for cluster 54 | onelineArm = convertToOneArmTemplateLine(file) 55 | 56 | print('Syntax: ARM template:') 57 | print('----------------------------') 58 | print('"customData": "[base64(concat(\'' + onelineArm + '\'))]\",') 59 | print() 60 | 61 | onelineBicep = convertToOneAzureBicepLine(file) 62 | print() 63 | print('Syntax: Azure Bicep') 64 | print('-------------------') 65 | print('customData: base64(\'' + onelineBicep + '\')') 66 | 67 | if __name__ == "__main__": 68 | main() 69 | --------------------------------------------------------------------------------