├── 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 |
--------------------------------------------------------------------------------