├── LICENSE
├── README.md
├── azuredeploy.json
├── frankenetes
├── apply_defaults.sh
├── create_certs.sh
├── create_kubeconfigs.sh
├── defaults
│ ├── ClusterRoleBinding.yml
│ └── LimitRange.yml
└── tls
│ ├── admin-csr.json
│ ├── ca-config.json
│ ├── ca-csr.json
│ ├── etcd-csr.json
│ ├── kube-controller-manager-csr.json
│ ├── kube-scheduler-csr.json
│ ├── kubernetes-csr.json
│ ├── virtual-kubelet-csr.json
│ └── virtual-kubelet-win-csr.json
└── parameters.sample.json
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Noel Bundick
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 | # frankenetes
2 |
3 | Serverless Kubernetes on Azure Container Instances
4 |
5 |
6 |
7 | ## Description
8 |
9 | This repo contains all the code needed to create a "virtual cluster" on top of Azure Container Instances. **This isn't production-grade** - think of it as a fun experiment and a reference on what's possible using Resource Manager Templates and ACI.
10 |
11 | Read more here:
12 | [https://www.noelbundick.com/2018/01/22/Frankenetes-Running-the-Kubernetes-control-plane-on-Azure-Container-Instances/](https://www.noelbundick.com/2018/01/22/Frankenetes-Running-the-Kubernetes-control-plane-on-Azure-Container-Instances/)
13 |
14 | ## Usage
15 |
16 | 1. Create a Service Principal for virtual-kubelet
17 |
18 | ```shell
19 | # Create a service principal
20 | # Tip: the following terms are synonymous: clientId<->appId, clientSecret<->password
21 | az ad sp create-for-rbac -n frankenetes --skip-assignment
22 | ```
23 |
24 | 2. Create a virtual Kubernetes cluster
25 |
26 | ```shell
27 | az group create -n frankenetes -l eastus
28 | az group deployment create -g frankenetes --template-file ./azuredeploy.json --parameters servicePrincipalClientId= servicePrincipalClientSecret= servicePrincipalObjectId= --query 'properties.outputs.kubeconfig.value' -o tsv
29 |
30 | # Download the kubeconfig from Azure Files
31 | # The exact command to run is an output of the deployment
32 | az storage file download --account-name -s kubeconfigs -p admin.kubeconfig
33 | export KUBECONFIG=admin.kubeconfig
34 | ```
35 |
36 | 3. Run something!
37 |
38 | Frankenetes runs two virtual-kubelets, so it's a hybrid-OS cluster!
39 |
40 | You can run Linux containers:
41 |
42 | ```shell
43 | # Run nginx
44 | kubectl run nginx --image=nginx --port 80
45 |
46 | # Hit the deployed pod
47 | NGINX_IP=`kubectl get pod -l run=nginx -o=jsonpath='{.items[0].status.podIP}'`
48 | curl $NGINX_IP
49 | ```
50 |
51 | Or Windows containers:
52 |
53 | ```shell
54 | # Run IIS
55 | k run iis --image=microsoft/iis:nanoserver-sac2016 --port=80
56 |
57 | # Hit the deployed pod
58 | IIS_IP=`kubectl get pod -l run=iis -o=jsonpath='{.items[0].status.podIP}'`
59 | curl $IIS_IP
60 | ```
61 |
62 | ## Cleanup
63 |
64 | To remove the cluster completely, delete the resource groups:
65 |
66 | ```shell
67 | az group delete -n frankenetes -y --no-wait
68 | ```
69 |
70 | To stop all compute, but leave your cluster configuration intact, delete just your Azure Container Instances:
71 |
72 | ```shell
73 | for aci in `az container list -g frankenetes --query "[].name" -o tsv`; do az container delete -n $aci -g frankenetes -y; done
74 | ```
--------------------------------------------------------------------------------
/azuredeploy.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "apiserverDnsNameLabel": {
6 | "type": "string",
7 | "defaultValue": "[toLower(concat('frankenetes-apiserver', take(uniqueString(resourceGroup().id), 6)))]"
8 | },
9 | "etcdDnsNameLabel": {
10 | "type": "string",
11 | "defaultValue": "[toLower(concat('frankenetes-etcd', take(uniqueString(resourceGroup().id), 6)))]"
12 | },
13 | "k8sVersion": {
14 | "type": "string",
15 | "defaultValue": "v1.9.2"
16 | },
17 | "vkPodResourceGroupName": {
18 | "type": "string",
19 | "defaultValue": "[resourceGroup().name]"
20 | },
21 | "servicePrincipalClientId": {
22 | "type": "string"
23 | },
24 | "servicePrincipalClientSecret": {
25 | "type": "securestring"
26 | },
27 | "servicePrincipalObjectId": {
28 | "type": "string"
29 | },
30 | "storageAccountName": {
31 | "type": "string",
32 | "defaultValue": "[toLower(concat('frankenetes', take(uniqueString(resourceGroup().id), 6)))]"
33 | }
34 | },
35 | "variables": {
36 | "contributorRole": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c')]",
37 | "etcdFQDN": "[concat(parameters('etcdDnsNameLabel'), '.', resourceGroup().location, '.azurecontainer.io')]",
38 | "apiserverFQDN": "[concat(parameters('apiserverDnsNameLabel'), '.', resourceGroup().location, '.azurecontainer.io')]",
39 | "sharesToCreate": "tls kubeconfigs etcd apiserver controller-manager scheduler virtual-kubelet",
40 | "vkConfig": "[concat('{\"clientId\": \"', parameters('servicePrincipalClientId'), '\",\"clientSecret\": \"', parameters('servicePrincipalClientSecret'), '\",\"subscriptionId\": \"', subscription().subscriptionId, '\",\"tenantId\": \"', subscription().tenantId, '\",\"activeDirectoryEndpointUrl\": \"https://login.microsoftonline.com/\",\"resourceManagerEndpointUrl\": \"https://management.azure.com/\",\"activeDirectoryGraphResourceId\": \"https://graph.windows.net/\",\"sqlManagementEndpointUrl\": \"database.windows.net\",\"galleryEndpointUrl\": \"https://gallery.azure.com/\",\"managementEndpointUrl\": \"https://management.core.windows.net/\"}')]",
41 | "storageApiVersion": "[providers('Microsoft.Storage', 'storageAccounts').apiVersions[0]]"
42 | },
43 | "resources": [
44 | {
45 | "type": "Microsoft.Authorization/roleAssignments",
46 | "apiVersion": "2015-07-01",
47 | "name": "[guid(resourceGroup().id)]",
48 | "properties": {
49 | "principalId": "[parameters('servicePrincipalObjectId')]",
50 | "roleDefinitionId": "[variables('contributorRole')]",
51 | "scope": "[resourceGroup().id]"
52 | }
53 | },
54 | {
55 | "type": "Microsoft.Storage/storageAccounts",
56 | "apiVersion": "2018-02-01",
57 | "location": "[resourceGroup().location]",
58 | "name": "[parameters('storageAccountName')]",
59 | "kind": "StorageV2",
60 | "sku": {
61 | "name": "Standard_LRS"
62 | },
63 | "properties": {
64 | "encryption": {
65 | "keySource": "Microsoft.Storage",
66 | "services": {
67 | "blob": {
68 | "enabled": true
69 | },
70 | "file": {
71 | "enabled": true
72 | }
73 | }
74 | },
75 | "supportsHttpsTrafficOnly": true
76 | }
77 | },
78 | {
79 | "type": "Microsoft.ContainerInstance/containerGroups",
80 | "apiVersion": "2018-06-01",
81 | "location": "[resourceGroup().location]",
82 | "dependsOn": [
83 | "[resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName'))]"
84 | ],
85 | "name": "setup",
86 | "properties": {
87 | "containers": [
88 | {
89 | "name": "setup",
90 | "properties": {
91 | "command": [
92 | "/bin/bash",
93 | "-c",
94 | "for i in $SHARES; do az storage share create -n $i; done"
95 | ],
96 | "environmentVariables": [
97 | {
98 | "name": "AZURE_STORAGE_ACCOUNT",
99 | "value": "[parameters('storageAccountName')]"
100 | },
101 | {
102 | "name": "AZURE_STORAGE_KEY",
103 | "value": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), variables('storageApiVersion')).keys[0].value]"
104 | },
105 | {
106 | "name": "SHARES",
107 | "value": "[variables('sharesToCreate')]"
108 | }
109 | ],
110 | "image": "microsoft/azure-cli",
111 | "resources": {
112 | "requests": {
113 | "cpu": 1,
114 | "memoryInGB": 1.5
115 | }
116 | }
117 | }
118 | }
119 | ],
120 | "osType": "Linux",
121 | "restartPolicy": "OnFailure"
122 | }
123 | },
124 | {
125 | "type": "Microsoft.ContainerInstance/containerGroups",
126 | "apiVersion": "2018-06-01",
127 | "location": "[resourceGroup().location]",
128 | "dependsOn": [
129 | "[resourceId('Microsoft.ContainerInstance/containerGroups', 'setup')]"
130 | ],
131 | "name": "cfssl",
132 | "properties": {
133 | "containers": [
134 | {
135 | "name": "cfssl",
136 | "properties": {
137 | "command": [
138 | "/bin/sh",
139 | "-c",
140 | "[concat('chmod +x /frankenetes/frankenetes/create_certs.sh && /frankenetes/frankenetes/create_certs.sh ', variables('etcdFQDN'), ' ', variables('apiserverFQDN'), ' && cp /frankenetes/frankenetes/tls/*.pem /tls')]"
141 | ],
142 | "image": "cfssl/cfssl",
143 | "resources": {
144 | "requests": {
145 | "cpu": 1,
146 | "memoryInGB": 1.5
147 | }
148 | },
149 | "volumeMounts": [
150 | {
151 | "name": "scripts",
152 | "mountPath": "/frankenetes"
153 | },
154 | {
155 | "name": "tls",
156 | "mountPath": "/tls"
157 | },
158 | {
159 | "name": "etcd",
160 | "mountPath": "/etcd"
161 | },
162 | {
163 | "name": "apiserver",
164 | "mountPath": "/apiserver"
165 | },
166 | {
167 | "name": "controllermanager",
168 | "mountPath": "/controllermanager"
169 | }
170 | ]
171 | }
172 | }
173 | ],
174 | "osType": "Linux",
175 | "restartPolicy": "OnFailure",
176 | "volumes": [
177 | {
178 | "name": "scripts",
179 | "gitRepo": {
180 | "repository": "https://github.com/noelbundick/frankenetes.git",
181 | "directory": "."
182 | }
183 | },
184 | {
185 | "name": "tls",
186 | "azureFile": {
187 | "shareName": "tls",
188 | "storageAccountName": "[parameters('storageAccountName')]",
189 | "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), variables('storageApiVersion')).keys[0].value]"
190 | }
191 | },
192 | {
193 | "name": "etcd",
194 | "azureFile": {
195 | "shareName": "etcd",
196 | "storageAccountName": "[parameters('storageAccountName')]",
197 | "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), variables('storageApiVersion')).keys[0].value]"
198 | }
199 | },
200 | {
201 | "name": "apiserver",
202 | "azureFile": {
203 | "shareName": "apiserver",
204 | "storageAccountName": "[parameters('storageAccountName')]",
205 | "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), variables('storageApiVersion')).keys[0].value]"
206 | }
207 | },
208 | {
209 | "name": "controllermanager",
210 | "azureFile": {
211 | "shareName": "controller-manager",
212 | "storageAccountName": "[parameters('storageAccountName')]",
213 | "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), variables('storageApiVersion')).keys[0].value]"
214 | }
215 | }
216 | ]
217 | }
218 | },
219 | {
220 | "type": "Microsoft.ContainerInstance/containerGroups",
221 | "apiVersion": "2018-06-01",
222 | "location": "[resourceGroup().location]",
223 | "dependsOn": [
224 | "[resourceId('Microsoft.ContainerInstance/containerGroups', 'cfssl')]"
225 | ],
226 | "name": "kubeconfig",
227 | "properties": {
228 | "containers": [
229 | {
230 | "name": "kubeconfig",
231 | "properties": {
232 | "command": [
233 | "/bin/sh",
234 | "-c",
235 | "[concat('chmod +x /frankenetes/frankenetes/create_kubeconfigs.sh && /frankenetes/frankenetes/create_kubeconfigs.sh ', variables('apiServerFQDN'), ' /tls /kubeconfigs')]"
236 | ],
237 | "image": "[concat('lachlanevenson/k8s-kubectl:', parameters('k8sVersion'))]",
238 | "resources": {
239 | "requests": {
240 | "cpu": 1,
241 | "memoryInGB": 1.5
242 | }
243 | },
244 | "volumeMounts": [
245 | {
246 | "name": "scripts",
247 | "mountPath": "/frankenetes"
248 | },
249 | {
250 | "name": "tls",
251 | "mountPath": "/tls"
252 | },
253 | {
254 | "name": "kubeconfigs",
255 | "mountPath": "/kubeconfigs"
256 | },
257 | {
258 | "name": "controllermanager",
259 | "mountPath": "/controllermanager"
260 | },
261 | {
262 | "name": "scheduler",
263 | "mountPath": "/scheduler"
264 | },
265 | {
266 | "name": "virtualkubelet",
267 | "mountPath": "/virtualkubelet"
268 | }
269 | ]
270 | }
271 | }
272 | ],
273 | "osType": "Linux",
274 | "restartPolicy": "OnFailure",
275 | "volumes": [
276 | {
277 | "name": "scripts",
278 | "gitRepo": {
279 | "repository": "https://github.com/noelbundick/frankenetes.git",
280 | "directory": "."
281 | }
282 | },
283 | {
284 | "name": "tls",
285 | "azureFile": {
286 | "shareName": "tls",
287 | "storageAccountName": "[parameters('storageAccountName')]",
288 | "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), variables('storageApiVersion')).keys[0].value]"
289 | }
290 | },
291 | {
292 | "name": "kubeconfigs",
293 | "azureFile": {
294 | "shareName": "kubeconfigs",
295 | "storageAccountName": "[parameters('storageAccountName')]",
296 | "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), variables('storageApiVersion')).keys[0].value]"
297 | }
298 | },
299 | {
300 | "name": "controllermanager",
301 | "azureFile": {
302 | "shareName": "controller-manager",
303 | "storageAccountName": "[parameters('storageAccountName')]",
304 | "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), variables('storageApiVersion')).keys[0].value]"
305 | }
306 | },
307 | {
308 | "name": "scheduler",
309 | "azureFile": {
310 | "shareName": "scheduler",
311 | "storageAccountName": "[parameters('storageAccountName')]",
312 | "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), variables('storageApiVersion')).keys[0].value]"
313 | }
314 | },
315 | {
316 | "name": "virtualkubelet",
317 | "azureFile": {
318 | "shareName": "virtual-kubelet",
319 | "storageAccountName": "[parameters('storageAccountName')]",
320 | "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), variables('storageApiVersion')).keys[0].value]"
321 | }
322 | }
323 | ]
324 | }
325 | },
326 | {
327 | "type": "Microsoft.ContainerInstance/containerGroups",
328 | "apiVersion": "2018-06-01",
329 | "location": "[resourceGroup().location]",
330 | "dependsOn": [
331 | "[resourceId('Microsoft.ContainerInstance/containerGroups', 'kubeconfig')]"
332 | ],
333 | "name": "etcd",
334 | "properties": {
335 | "containers": [
336 | {
337 | "name": "etcd",
338 | "properties": {
339 | "command": [
340 | "/usr/local/bin/etcd",
341 | "--name=frankenetes-etcd-0",
342 | "--cert-file=/etcd/etcd.pem",
343 | "--key-file=/etcd/etcd-key.pem",
344 | "--trusted-ca-file=/etcd/ca.pem",
345 | "--client-cert-auth",
346 | "--listen-client-urls=https://0.0.0.0:2379",
347 | "[concat('--advertise-client-urls=https://', variables('etcdFQDN'), ':2379')]",
348 | "--data-dir=/etcd/data",
349 | "--wal-dir=/etcd-wal"
350 | ],
351 | "image": "quay.io/coreos/etcd:v3.2.8",
352 | "ports": [
353 | {
354 | "port": 2379,
355 | "protocol": "TCP"
356 | },
357 | {
358 | "port": 2380,
359 | "protocol": "TCP"
360 | }
361 | ],
362 | "resources": {
363 | "requests": {
364 | "cpu": 1,
365 | "memoryInGB": 1.5
366 | }
367 | },
368 | "volumeMounts": [
369 | {
370 | "name": "azurefile",
371 | "mountPath": "/etcd"
372 | }
373 | ]
374 | }
375 | }
376 | ],
377 | "ipAddress": {
378 | "dnsNameLabel": "[parameters('etcdDnsNameLabel')]",
379 | "type": "Public",
380 | "ports": [
381 | {
382 | "port": 2379,
383 | "protocol": "TCP"
384 | },
385 | {
386 | "port": 2380,
387 | "protocol": "TCP"
388 | }
389 | ]
390 | },
391 | "osType": "Linux",
392 | "restartPolicy": "Always",
393 | "volumes": [
394 | {
395 | "name": "azurefile",
396 | "azureFile": {
397 | "shareName": "etcd",
398 | "storageAccountName": "[parameters('storageAccountName')]",
399 | "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), variables('storageApiVersion')).keys[0].value]"
400 | }
401 | }
402 | ]
403 | }
404 | },
405 | {
406 | "type": "Microsoft.ContainerInstance/containerGroups",
407 | "apiVersion": "2018-06-01",
408 | "location": "[resourceGroup().location]",
409 | "dependsOn": [
410 | "[resourceId('Microsoft.ContainerInstance/containerGroups', 'kubeconfig')]"
411 | ],
412 | "name": "apiserver",
413 | "properties": {
414 | "containers": [
415 | {
416 | "name": "apiserver",
417 | "properties": {
418 | "command": [
419 | "/apiserver",
420 | "--admission-control=NamespaceLifecycle,LimitRanger,ResourceQuota",
421 | "--advertise-address=0.0.0.0",
422 | "--allow-privileged=false",
423 | "--apiserver-count=1",
424 | "--audit-log-maxage=30",
425 | "--audit-log-maxbackup=3",
426 | "--audit-log-maxsize=100",
427 | "--audit-log-path=/apisrv/log/audit.log",
428 | "--authorization-mode=Node,RBAC",
429 | "--bind-address=0.0.0.0",
430 | "--client-ca-file=/apisrv/ca.pem",
431 | "--enable-swagger-ui=true",
432 | "--etcd-cafile=/apisrv/ca.pem",
433 | "--etcd-certfile=/apisrv/etcd.pem",
434 | "--etcd-keyfile=/apisrv/etcd-key.pem",
435 | "[concat('--etcd-servers=https://', variables('etcdFQDN'), ':2379')]",
436 | "--event-ttl=1h",
437 | "--insecure-bind-address=127.0.0.1",
438 | "--kubelet-certificate-authority=/apisrv/ca.pem",
439 | "--kubelet-client-certificate=/apisrv/kubernetes.pem",
440 | "--kubelet-client-key=/apisrv/kubernetes-key.pem",
441 | "--kubelet-https=true",
442 | "--runtime-config=api/all",
443 | "--runtime-config=admissionregistration.k8s.io/v1alpha1",
444 | "--service-account-key-file=/apisrv/ca-key.pem",
445 | "--service-node-port-range=30000-32767",
446 | "--tls-ca-file=/apisrv/ca.pem",
447 | "--tls-cert-file=/apisrv/kubernetes.pem",
448 | "--tls-private-key-file=/apisrv/kubernetes-key.pem",
449 | "--v=2"
450 | ],
451 | "image": "[concat('gcr.io/google-containers/hyperkube-amd64:', parameters('k8sVersion'))]",
452 | "ports": [
453 | {
454 | "port": 6443,
455 | "protocol": "TCP"
456 | }
457 | ],
458 | "resources": {
459 | "requests": {
460 | "cpu": 1,
461 | "memoryInGB": 1.5
462 | }
463 | },
464 | "volumeMounts": [
465 | {
466 | "name": "azurefile",
467 | "mountPath": "/apisrv"
468 | }
469 | ]
470 | }
471 | }
472 | ],
473 | "ipAddress": {
474 | "dnsNameLabel": "[parameters('apiserverDnsNameLabel')]",
475 | "type": "Public",
476 | "ports": [
477 | {
478 | "port": 6443,
479 | "protocol": "TCP"
480 | }
481 | ]
482 | },
483 | "osType": "Linux",
484 | "restartPolicy": "Always",
485 | "volumes": [
486 | {
487 | "name": "azurefile",
488 | "azureFile": {
489 | "shareName": "apiserver",
490 | "storageAccountName": "[parameters('storageAccountName')]",
491 | "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), variables('storageApiVersion')).keys[0].value]"
492 | }
493 | }
494 | ]
495 | }
496 | },
497 | {
498 | "type": "Microsoft.ContainerInstance/containerGroups",
499 | "apiVersion": "2018-06-01",
500 | "location": "[resourceGroup().location]",
501 | "dependsOn": [
502 | "[resourceId('Microsoft.ContainerInstance/containerGroups', 'kubeconfig')]"
503 | ],
504 | "name": "controllermanager",
505 | "properties": {
506 | "containers": [
507 | {
508 | "name": "controllermanager",
509 | "properties": {
510 | "command": [
511 | "/controller-manager",
512 | "--address=0.0.0.0",
513 | "--cluster-cidr=10.200.0.0/16",
514 | "--cluster-name=kubernetes",
515 | "--cluster-signing-cert-file=/cm/ca.pem",
516 | "--cluster-signing-key-file=/cm/ca-key.pem",
517 | "--kubeconfig=/cm/controller-manager.kubeconfig",
518 | "--leader-elect=true",
519 | "--root-ca-file=/cm/ca.pem",
520 | "--service-account-private-key-file=/cm/ca-key.pem",
521 | "--use-service-account-credentials",
522 | "--v=2"
523 | ],
524 | "image": "[concat('gcr.io/google-containers/hyperkube-amd64:', parameters('k8sVersion'))]",
525 | "resources": {
526 | "requests": {
527 | "cpu": 1,
528 | "memoryInGB": 1.5
529 | }
530 | },
531 | "volumeMounts": [
532 | {
533 | "name": "azurefile",
534 | "mountPath": "/cm"
535 | }
536 | ]
537 | }
538 | }
539 | ],
540 | "osType": "Linux",
541 | "restartPolicy": "Always",
542 | "volumes": [
543 | {
544 | "name": "azurefile",
545 | "azureFile": {
546 | "shareName": "controller-manager",
547 | "storageAccountName": "[parameters('storageAccountName')]",
548 | "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), variables('storageApiVersion')).keys[0].value]"
549 | }
550 | }
551 | ]
552 | }
553 | },
554 | {
555 | "type": "Microsoft.ContainerInstance/containerGroups",
556 | "apiVersion": "2018-06-01",
557 | "location": "[resourceGroup().location]",
558 | "dependsOn": [
559 | "[resourceId('Microsoft.ContainerInstance/containerGroups', 'kubeconfig')]"
560 | ],
561 | "name": "scheduler",
562 | "properties": {
563 | "containers": [
564 | {
565 | "name": "scheduler",
566 | "properties": {
567 | "command": [
568 | "/scheduler",
569 | "--kubeconfig=/sched/scheduler.kubeconfig",
570 | "--leader-elect=true",
571 | "--v=2"
572 | ],
573 | "image": "[concat('gcr.io/google-containers/hyperkube-amd64:', parameters('k8sVersion'))]",
574 | "resources": {
575 | "requests": {
576 | "cpu": 1,
577 | "memoryInGB": 1.5
578 | }
579 | },
580 | "volumeMounts": [
581 | {
582 | "name": "azurefile",
583 | "mountPath": "/sched"
584 | }
585 | ]
586 | }
587 | }
588 | ],
589 | "osType": "Linux",
590 | "restartPolicy": "Always",
591 | "volumes": [
592 | {
593 | "name": "azurefile",
594 | "azureFile": {
595 | "shareName": "scheduler",
596 | "storageAccountName": "[parameters('storageAccountName')]",
597 | "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), variables('storageApiVersion')).keys[0].value]"
598 | }
599 | }
600 | ]
601 | }
602 | },
603 | {
604 | "type": "Microsoft.ContainerInstance/containerGroups",
605 | "apiVersion": "2018-06-01",
606 | "location": "[resourceGroup().location]",
607 | "dependsOn": [
608 | "[resourceId('Microsoft.ContainerInstance/containerGroups', 'apiserver')]"
609 | ],
610 | "name": "defaults",
611 | "properties": {
612 | "containers": [
613 | {
614 | "name": "defaults",
615 | "properties": {
616 | "command": [
617 | "/bin/sh",
618 | "-c",
619 | "chmod +x /frankenetes/frankenetes/apply_defaults.sh && /frankenetes/frankenetes/apply_defaults.sh /kubeconfigs/admin.kubeconfig"
620 | ],
621 | "image": "[concat('lachlanevenson/k8s-kubectl:', parameters('k8sVersion'))]",
622 | "resources": {
623 | "requests": {
624 | "cpu": 1,
625 | "memoryInGB": 1.5
626 | }
627 | },
628 | "volumeMounts": [
629 | {
630 | "name": "scripts",
631 | "mountPath": "/frankenetes"
632 | },
633 | {
634 | "name": "kubeconfigs",
635 | "mountPath": "/kubeconfigs"
636 | }
637 | ]
638 | }
639 | }
640 | ],
641 | "osType": "Linux",
642 | "restartPolicy": "OnFailure",
643 | "volumes": [
644 | {
645 | "name": "scripts",
646 | "gitRepo": {
647 | "repository": "https://github.com/noelbundick/frankenetes.git",
648 | "directory": "."
649 | }
650 | },
651 | {
652 | "name": "kubeconfigs",
653 | "azureFile": {
654 | "shareName": "kubeconfigs",
655 | "storageAccountName": "[parameters('storageAccountName')]",
656 | "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), variables('storageApiVersion')).keys[0].value]"
657 | }
658 | }
659 | ]
660 | }
661 | },
662 | {
663 | "type": "Microsoft.ContainerInstance/containerGroups",
664 | "apiVersion": "2018-06-01",
665 | "location": "[resourceGroup().location]",
666 | "dependsOn": [
667 | "[resourceId('Microsoft.ContainerInstance/containerGroups', 'defaults')]"
668 | ],
669 | "name": "virtual-kubelet",
670 | "properties": {
671 | "containers": [
672 | {
673 | "name": "virtual-kubelet",
674 | "properties": {
675 | "command": [
676 | "/usr/bin/virtual-kubelet",
677 | "--kubeconfig", "/etc/virtual-kubelet/virtual-kubelet.kubeconfig",
678 | "--nodename", "virtual-kubelet",
679 | "--os", "Linux",
680 | "--provider", "azure"
681 | ],
682 | "environmentVariables": [
683 | {
684 | "name": "AZURE_AUTH_LOCATION",
685 | "value": "/etc/virtual-kubelet/secret/credentials.json"
686 | },
687 | {
688 | "name": "ACI_RESOURCE_GROUP",
689 | "value": "[parameters('vkPodResourceGroupName')]"
690 | },
691 | {
692 | "name": "ACI_REGION",
693 | "value": "[resourceGroup().location]"
694 | }
695 | ],
696 | "image": "microsoft/virtual-kubelet",
697 | "resources": {
698 | "requests": {
699 | "cpu": 1,
700 | "memoryInGB": 1.5
701 | }
702 | },
703 | "volumeMounts": [
704 | {
705 | "name": "azurefile",
706 | "mountPath": "/etc/virtual-kubelet"
707 | },
708 | {
709 | "name": "secret",
710 | "mountPath": "/etc/virtual-kubelet/secret"
711 | }
712 | ]
713 | }
714 | },
715 | {
716 | "name": "virtual-kubelet-win",
717 | "properties": {
718 | "command": [
719 | "/usr/bin/virtual-kubelet",
720 | "--kubeconfig", "/etc/virtual-kubelet/virtual-kubelet-win.kubeconfig",
721 | "--nodename", "virtual-kubelet-win",
722 | "--os", "Windows",
723 | "--provider", "azure"
724 | ],
725 | "environmentVariables": [
726 | {
727 | "name": "AZURE_AUTH_LOCATION",
728 | "value": "/etc/virtual-kubelet/secret/credentials.json"
729 | },
730 | {
731 | "name": "ACI_RESOURCE_GROUP",
732 | "value": "[parameters('vkPodResourceGroupName')]"
733 | },
734 | {
735 | "name": "ACI_REGION",
736 | "value": "[resourceGroup().location]"
737 | }
738 | ],
739 | "image": "microsoft/virtual-kubelet",
740 | "resources": {
741 | "requests": {
742 | "cpu": 1,
743 | "memoryInGB": 1.5
744 | }
745 | },
746 | "volumeMounts": [
747 | {
748 | "name": "azurefile",
749 | "mountPath": "/etc/virtual-kubelet"
750 | },
751 | {
752 | "name": "secret",
753 | "mountPath": "/etc/virtual-kubelet/secret"
754 | }
755 | ]
756 | }
757 | }
758 | ],
759 | "osType": "Linux",
760 | "restartPolicy": "Always",
761 | "volumes": [
762 | {
763 | "name": "azurefile",
764 | "azureFile": {
765 | "shareName": "virtual-kubelet",
766 | "storageAccountName": "[parameters('storageAccountName')]",
767 | "storageAccountKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', parameters('storageAccountName')), variables('storageApiVersion')).keys[0].value]"
768 | }
769 | },
770 | {
771 | "name": "secret",
772 | "secret": {
773 | "credentials.json": "[base64(variables('vkConfig'))]"
774 | }
775 | }
776 | ]
777 | }
778 | }
779 | ],
780 | "outputs": {
781 | "kubeconfig": {
782 | "type": "string",
783 | "value": "[concat('az storage file download --account-name ', parameters('storageAccountName'), ' -s kubeconfigs -p admin.kubeconfig')]"
784 | }
785 | }
786 | }
--------------------------------------------------------------------------------
/frankenetes/apply_defaults.sh:
--------------------------------------------------------------------------------
1 | export KUBECONFIG=$1
2 |
3 | # Execute in the defaults directory
4 | cd `dirname $0`/defaults
5 |
6 | kubectl apply -f ClusterRoleBinding.yml
7 | kubectl apply -f LimitRange.yml
8 |
--------------------------------------------------------------------------------
/frankenetes/create_certs.sh:
--------------------------------------------------------------------------------
1 | ETCD_FQDN=$1
2 | APISERVER_FQDN=$2
3 |
4 | # Execute in the tls directory
5 | cd `dirname $0`/tls
6 |
7 | # Create a new Certificate Authority
8 | cfssl gencert -initca ca-csr.json | cfssljson -bare ca
9 |
10 | # TODO: Use different CA's for etcd vs k8s
11 | # etcd
12 | cfssl gencert \
13 | -ca=ca.pem \
14 | -ca-key=ca-key.pem \
15 | -config=ca-config.json \
16 | -hostname=$ETCD_FQDN,127.0.0.1 \
17 | -profile=kubernetes \
18 | etcd-csr.json | cfssljson -bare etcd
19 | cp etcd.pem etcd-key.pem ca.pem /etcd
20 |
21 | # apiserver
22 | cfssl gencert \
23 | -ca=ca.pem \
24 | -ca-key=ca-key.pem \
25 | -config=ca-config.json \
26 | -hostname=$APISERVER_FQDN,127.0.0.1,kubernetes.default \
27 | -profile=kubernetes \
28 | kubernetes-csr.json | cfssljson -bare kubernetes
29 | cp kubernetes.pem kubernetes-key.pem etcd.pem etcd-key.pem ca.pem ca-key.pem /apiserver
30 |
31 | # Generate the admin client certificate
32 | cfssl gencert \
33 | -ca=ca.pem \
34 | -ca-key=ca-key.pem \
35 | -config=ca-config.json \
36 | -profile=kubernetes \
37 | admin-csr.json | cfssljson -bare admin
38 |
39 | # kube-controller-manager
40 | cfssl gencert \
41 | -ca=ca.pem \
42 | -ca-key=ca-key.pem \
43 | -config=ca-config.json \
44 | -profile=kubernetes \
45 | kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager
46 | cp ca.pem ca-key.pem /controllermanager
47 |
48 | # kube-scheduler
49 | cfssl gencert \
50 | -ca=ca.pem \
51 | -ca-key=ca-key.pem \
52 | -config=ca-config.json \
53 | -profile=kubernetes \
54 | kube-scheduler-csr.json | cfssljson -bare kube-scheduler
55 |
56 | # virtual-kubelet node
57 | cfssl gencert \
58 | -ca=ca.pem \
59 | -ca-key=ca-key.pem \
60 | -config=ca-config.json \
61 | -hostname=virtual-kubelet \
62 | -profile=kubernetes \
63 | virtual-kubelet-csr.json | cfssljson -bare virtual-kubelet
64 |
65 | # virtual-kubelet-win node
66 | cfssl gencert \
67 | -ca=ca.pem \
68 | -ca-key=ca-key.pem \
69 | -config=ca-config.json \
70 | -hostname=virtual-kubelet-win \
71 | -profile=kubernetes \
72 | virtual-kubelet-win-csr.json | cfssljson -bare virtual-kubelet-win
--------------------------------------------------------------------------------
/frankenetes/create_kubeconfigs.sh:
--------------------------------------------------------------------------------
1 | APISERVER_FQDN=$1
2 | CERTS_DIR=$2
3 | OUTPUT_DIR=$3
4 |
5 | ############
6 | # create admin kubeconfig
7 | ############
8 | ADMIN_KUBECONFIG=$OUTPUT_DIR/admin.kubeconfig
9 |
10 | kubectl config set-cluster frankenetes \
11 | --certificate-authority=$CERTS_DIR/ca.pem \
12 | --embed-certs=true \
13 | --server="https://$APISERVER_FQDN:6443" \
14 | --kubeconfig=$ADMIN_KUBECONFIG
15 | kubectl config set-credentials admin \
16 | --client-certificate=$CERTS_DIR/admin.pem \
17 | --client-key=$CERTS_DIR/admin-key.pem \
18 | --embed-certs=true \
19 | --kubeconfig=$ADMIN_KUBECONFIG
20 | kubectl config set-context frankenetes \
21 | --cluster=frankenetes \
22 | --user=admin \
23 | --kubeconfig=$ADMIN_KUBECONFIG
24 | kubectl config use-context frankenetes \
25 | --kubeconfig=$ADMIN_KUBECONFIG
26 |
27 | echo "admin kubeconfig created at $ADMIN_KUBECONFIG"
28 |
29 | ############
30 | # create controller manager kubeconfig
31 | ############
32 | CONTROLLER_MANAGER_KUBECONFIG=$OUTPUT_DIR/controller-manager.kubeconfig
33 |
34 | kubectl config set-cluster frankenetes \
35 | --certificate-authority=$CERTS_DIR/ca.pem \
36 | --embed-certs=true \
37 | --server="https://$APISERVER_FQDN:6443" \
38 | --kubeconfig=$CONTROLLER_MANAGER_KUBECONFIG
39 | kubectl config set-credentials controller-manager \
40 | --client-certificate=$CERTS_DIR/kube-controller-manager.pem \
41 | --client-key=$CERTS_DIR/kube-controller-manager-key.pem \
42 | --embed-certs=true \
43 | --kubeconfig=$CONTROLLER_MANAGER_KUBECONFIG
44 | kubectl config set-context frankenetes \
45 | --cluster=frankenetes \
46 | --user=controller-manager \
47 | --kubeconfig=$CONTROLLER_MANAGER_KUBECONFIG
48 | kubectl config use-context frankenetes \
49 | --kubeconfig=$CONTROLLER_MANAGER_KUBECONFIG
50 |
51 | echo "controller manager kubeconfig created at $CONTROLLER_MANAGER_KUBECONFIG"
52 | cp $CONTROLLER_MANAGER_KUBECONFIG /controllermanager/controller-manager.kubeconfig
53 |
54 | ############
55 | # create scheduler kubeconfig
56 | ############
57 | SCHEDULER_KUBECONFIG=$OUTPUT_DIR/scheduler.kubeconfig
58 |
59 | kubectl config set-cluster frankenetes \
60 | --certificate-authority=$CERTS_DIR/ca.pem \
61 | --embed-certs=true \
62 | --server="https://$APISERVER_FQDN:6443" \
63 | --kubeconfig=$SCHEDULER_KUBECONFIG
64 | kubectl config set-credentials scheduler \
65 | --client-certificate=$CERTS_DIR/kube-scheduler.pem \
66 | --client-key=$CERTS_DIR/kube-scheduler-key.pem \
67 | --embed-certs=true \
68 | --kubeconfig=$SCHEDULER_KUBECONFIG
69 | kubectl config set-context frankenetes \
70 | --cluster=frankenetes \
71 | --user=scheduler \
72 | --kubeconfig=$SCHEDULER_KUBECONFIG
73 | kubectl config use-context frankenetes \
74 | --kubeconfig=$SCHEDULER_KUBECONFIG
75 |
76 | echo "scheduler kubeconfig created at $SCHEDULER_KUBECONFIG"
77 | cp $SCHEDULER_KUBECONFIG /scheduler/scheduler.kubeconfig
78 |
79 | ############
80 | # create virtual-kubelet kubeconfig
81 | ############
82 | VK_KUBECONFIG=$OUTPUT_DIR/virtual-kubelet.kubeconfig
83 |
84 | kubectl config set-cluster frankenetes \
85 | --certificate-authority=$CERTS_DIR/ca.pem \
86 | --embed-certs=true \
87 | --server="https://$APISERVER_FQDN:6443" \
88 | --kubeconfig=$VK_KUBECONFIG
89 | kubectl config set-credentials system:node:virtual-kubelet \
90 | --client-certificate=$CERTS_DIR/virtual-kubelet.pem \
91 | --client-key=$CERTS_DIR/virtual-kubelet-key.pem \
92 | --embed-certs=true \
93 | --kubeconfig=$VK_KUBECONFIG
94 | kubectl config set-context frankenetes \
95 | --cluster=frankenetes \
96 | --user=system:node:virtual-kubelet \
97 | --kubeconfig=$VK_KUBECONFIG
98 | kubectl config use-context frankenetes \
99 | --kubeconfig=$VK_KUBECONFIG
100 |
101 | echo "virtual-kubelet kubeconfig created at $VK_KUBECONFIG"
102 | cp $VK_KUBECONFIG /virtualkubelet/virtual-kubelet.kubeconfig
103 |
104 | ############
105 | # create virtual-kubelet-win kubeconfig
106 | ############
107 | VK_WIN_KUBECONFIG=$OUTPUT_DIR/virtual-kubelet-win.kubeconfig
108 |
109 | kubectl config set-cluster frankenetes \
110 | --certificate-authority=$CERTS_DIR/ca.pem \
111 | --embed-certs=true \
112 | --server="https://$APISERVER_FQDN:6443" \
113 | --kubeconfig=$VK_WIN_KUBECONFIG
114 | kubectl config set-credentials system:node:virtual-kubelet-win \
115 | --client-certificate=$CERTS_DIR/virtual-kubelet-win.pem \
116 | --client-key=$CERTS_DIR/virtual-kubelet-win-key.pem \
117 | --embed-certs=true \
118 | --kubeconfig=$VK_WIN_KUBECONFIG
119 | kubectl config set-context frankenetes \
120 | --cluster=frankenetes \
121 | --user=system:node:virtual-kubelet-win \
122 | --kubeconfig=$VK_WIN_KUBECONFIG
123 | kubectl config use-context frankenetes \
124 | --kubeconfig=$VK_WIN_KUBECONFIG
125 |
126 | echo "virtual-kubelet-win kubeconfig created at $VK_WIN_KUBECONFIG"
127 | cp $VK_WIN_KUBECONFIG /virtualkubelet/virtual-kubelet-win.kubeconfig
--------------------------------------------------------------------------------
/frankenetes/defaults/ClusterRoleBinding.yml:
--------------------------------------------------------------------------------
1 | # See https://github.com/virtual-kubelet/virtual-kubelet/issues/210
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRoleBinding
4 | metadata:
5 | name: virtual-kubelet
6 | roleRef:
7 | apiGroup: rbac.authorization.k8s.io
8 | kind: ClusterRole
9 | name: cluster-admin
10 | subjects:
11 | - apiGroup: rbac.authorization.k8s.io
12 | kind: User
13 | name: system:node:virtual-kubelet
14 | - apiGroup: rbac.authorization.k8s.io
15 | kind: User
16 | name: system:node:virtual-kubelet-win
--------------------------------------------------------------------------------
/frankenetes/defaults/LimitRange.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: LimitRange
3 | metadata:
4 | name: vk-default-request
5 | namespace: kube-system
6 | spec:
7 | limits:
8 | - defaultRequest:
9 | cpu: 1
10 | memory: 1G
11 | type: Container
12 | ---
13 | apiVersion: v1
14 | kind: LimitRange
15 | metadata:
16 | name: vk-default-request
17 | namespace: kube-public
18 | spec:
19 | limits:
20 | - defaultRequest:
21 | cpu: 1
22 | memory: 1G
23 | type: Container
24 | ---
25 | apiVersion: v1
26 | kind: LimitRange
27 | metadata:
28 | name: vk-default-request
29 | namespace: default
30 | spec:
31 | limits:
32 | - defaultRequest:
33 | cpu: 1
34 | memory: 1G
35 | type: Container
--------------------------------------------------------------------------------
/frankenetes/tls/admin-csr.json:
--------------------------------------------------------------------------------
1 | {
2 | "CN": "admin",
3 | "key": {
4 | "algo": "rsa",
5 | "size": 2048
6 | },
7 | "names": [
8 | {
9 | "C": "US",
10 | "L": "Redmond",
11 | "O": "system:masters",
12 | "OU": "Frankenetes",
13 | "ST": "Washington"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/frankenetes/tls/ca-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "signing": {
3 | "default": {
4 | "expiry": "8760h"
5 | },
6 | "profiles": {
7 | "kubernetes": {
8 | "usages": ["signing", "key encipherment", "server auth", "client auth"],
9 | "expiry": "8760h"
10 | }
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/frankenetes/tls/ca-csr.json:
--------------------------------------------------------------------------------
1 | {
2 | "CN": "Kubernetes",
3 | "key": {
4 | "algo": "rsa",
5 | "size": 2048
6 | },
7 | "names": [
8 | {
9 | "C": "US",
10 | "L": "Redmond",
11 | "O": "Kubernetes",
12 | "OU": "CA",
13 | "ST": "Washington"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/frankenetes/tls/etcd-csr.json:
--------------------------------------------------------------------------------
1 | {
2 | "CN": "etcd",
3 | "key": {
4 | "algo": "rsa",
5 | "size": 2048
6 | },
7 | "names": [
8 | {
9 | "C": "US",
10 | "L": "Redmond",
11 | "O": "Kubernetes",
12 | "OU": "Frankenetes",
13 | "ST": "Washington"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/frankenetes/tls/kube-controller-manager-csr.json:
--------------------------------------------------------------------------------
1 | {
2 | "CN": "system:kube-controller-manager",
3 | "key": {
4 | "algo": "rsa",
5 | "size": 2048
6 | },
7 | "names": [
8 | {
9 | "C": "US",
10 | "L": "Redmond",
11 | "O": "system:kube-controller-manager",
12 | "OU": "Frankenetes",
13 | "ST": "Washington"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/frankenetes/tls/kube-scheduler-csr.json:
--------------------------------------------------------------------------------
1 | {
2 | "CN": "system:kube-scheduler",
3 | "key": {
4 | "algo": "rsa",
5 | "size": 2048
6 | },
7 | "names": [
8 | {
9 | "C": "US",
10 | "L": "Redmond",
11 | "O": "system:kube-scheduler",
12 | "OU": "Frankenetes",
13 | "ST": "Washington"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/frankenetes/tls/kubernetes-csr.json:
--------------------------------------------------------------------------------
1 | {
2 | "CN": "kubernetes",
3 | "key": {
4 | "algo": "rsa",
5 | "size": 2048
6 | },
7 | "names": [
8 | {
9 | "C": "US",
10 | "L": "Redmond",
11 | "O": "Kubernetes",
12 | "OU": "Frankenetes",
13 | "ST": "Washington"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/frankenetes/tls/virtual-kubelet-csr.json:
--------------------------------------------------------------------------------
1 | {
2 | "CN": "system:node:virtual-kubelet",
3 | "key": {
4 | "algo": "rsa",
5 | "size": 2048
6 | },
7 | "names": [
8 | {
9 | "C": "US",
10 | "L": "Redmond",
11 | "O": "system:nodes",
12 | "OU": "Frankenetes",
13 | "ST": "Washington"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/frankenetes/tls/virtual-kubelet-win-csr.json:
--------------------------------------------------------------------------------
1 | {
2 | "CN": "system:node:virtual-kubelet-win",
3 | "key": {
4 | "algo": "rsa",
5 | "size": 2048
6 | },
7 | "names": [
8 | {
9 | "C": "US",
10 | "L": "Redmond",
11 | "O": "system:nodes",
12 | "OU": "Frankenetes",
13 | "ST": "Washington"
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/parameters.sample.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
3 | "contentVersion": "1.0.0.0",
4 | "parameters": {
5 | "servicePrincipalClientId": {
6 | "value": "d191834b-851f-4428-9612-817c8ad351b7"
7 | },
8 | "servicePrincipalObjectId": {
9 | "value": "b569938f-9689-4aa0-b434-43ae2d5c5ccb"
10 | },
11 | "servicePrincipalClientSecret": {
12 | "value": "dcf7b153-7599-4fc8-9d98-2782612d097e"
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------