github.com/mmcquillan/packer@v1.1.1-0.20171009221028-c85cf0483a5d/builder/azure/common/template/template_builder.go (about) 1 package template 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "strings" 7 8 "github.com/Azure/azure-sdk-for-go/arm/compute" 9 "github.com/Azure/go-autorest/autorest/to" 10 ) 11 12 const ( 13 jsonPrefix = "" 14 jsonIndent = " " 15 16 resourceKeyVaults = "Microsoft.KeyVault/vaults" 17 resourceManagedDisk = "Microsoft.Compute/images" 18 resourceNetworkInterfaces = "Microsoft.Network/networkInterfaces" 19 resourcePublicIPAddresses = "Microsoft.Network/publicIPAddresses" 20 resourceVirtualMachine = "Microsoft.Compute/virtualMachines" 21 resourceVirtualNetworks = "Microsoft.Network/virtualNetworks" 22 23 variableSshKeyPath = "sshKeyPath" 24 ) 25 26 type TemplateBuilder struct { 27 template *Template 28 osType compute.OperatingSystemTypes 29 } 30 31 func NewTemplateBuilder(template string) (*TemplateBuilder, error) { 32 var t Template 33 34 err := json.Unmarshal([]byte(template), &t) 35 if err != nil { 36 return nil, err 37 } 38 39 return &TemplateBuilder{ 40 template: &t, 41 }, nil 42 } 43 44 func (s *TemplateBuilder) BuildLinux(sshAuthorizedKey string) error { 45 resource, err := s.getResourceByType(resourceVirtualMachine) 46 if err != nil { 47 return err 48 } 49 50 profile := resource.Properties.OsProfile 51 profile.LinuxConfiguration = &compute.LinuxConfiguration{ 52 SSH: &compute.SSHConfiguration{ 53 PublicKeys: &[]compute.SSHPublicKey{ 54 { 55 Path: to.StringPtr(s.toVariable(variableSshKeyPath)), 56 KeyData: to.StringPtr(sshAuthorizedKey), 57 }, 58 }, 59 }, 60 } 61 62 s.osType = compute.Linux 63 return nil 64 } 65 66 func (s *TemplateBuilder) BuildWindows(keyVaultName, winRMCertificateUrl string) error { 67 resource, err := s.getResourceByType(resourceVirtualMachine) 68 if err != nil { 69 return err 70 } 71 72 profile := resource.Properties.OsProfile 73 74 profile.Secrets = &[]compute.VaultSecretGroup{ 75 { 76 SourceVault: &compute.SubResource{ 77 ID: to.StringPtr(s.toResourceID(resourceKeyVaults, keyVaultName)), 78 }, 79 VaultCertificates: &[]compute.VaultCertificate{ 80 { 81 CertificateStore: to.StringPtr("My"), 82 CertificateURL: to.StringPtr(winRMCertificateUrl), 83 }, 84 }, 85 }, 86 } 87 88 profile.WindowsConfiguration = &compute.WindowsConfiguration{ 89 ProvisionVMAgent: to.BoolPtr(true), 90 WinRM: &compute.WinRMConfiguration{ 91 Listeners: &[]compute.WinRMListener{ 92 { 93 Protocol: "https", 94 CertificateURL: to.StringPtr(winRMCertificateUrl), 95 }, 96 }, 97 }, 98 } 99 100 s.osType = compute.Windows 101 return nil 102 } 103 104 func (s *TemplateBuilder) SetManagedDiskUrl(managedImageId string, storageAccountType compute.StorageAccountTypes) error { 105 resource, err := s.getResourceByType(resourceVirtualMachine) 106 if err != nil { 107 return err 108 } 109 110 profile := resource.Properties.StorageProfile 111 profile.ImageReference = &compute.ImageReference{ 112 ID: &managedImageId, 113 } 114 profile.OsDisk.Name = to.StringPtr("osdisk") 115 profile.OsDisk.OsType = s.osType 116 profile.OsDisk.CreateOption = compute.FromImage 117 profile.OsDisk.Vhd = nil 118 profile.OsDisk.ManagedDisk = &compute.ManagedDiskParameters{ 119 StorageAccountType: storageAccountType, 120 } 121 122 return nil 123 } 124 125 func (s *TemplateBuilder) SetManagedMarketplaceImage(location, publisher, offer, sku, version, imageID string, storageAccountType compute.StorageAccountTypes) error { 126 resource, err := s.getResourceByType(resourceVirtualMachine) 127 if err != nil { 128 return err 129 } 130 131 profile := resource.Properties.StorageProfile 132 profile.ImageReference = &compute.ImageReference{ 133 Publisher: &publisher, 134 Offer: &offer, 135 Sku: &sku, 136 Version: &version, 137 //ID: &imageID, 138 } 139 profile.OsDisk.Name = to.StringPtr("osdisk") 140 profile.OsDisk.OsType = s.osType 141 profile.OsDisk.CreateOption = compute.FromImage 142 profile.OsDisk.Vhd = nil 143 profile.OsDisk.ManagedDisk = &compute.ManagedDiskParameters{ 144 StorageAccountType: storageAccountType, 145 } 146 147 return nil 148 } 149 150 func (s *TemplateBuilder) SetMarketPlaceImage(publisher, offer, sku, version string) error { 151 resource, err := s.getResourceByType(resourceVirtualMachine) 152 if err != nil { 153 return err 154 } 155 156 profile := resource.Properties.StorageProfile 157 profile.ImageReference = &compute.ImageReference{ 158 Publisher: to.StringPtr(publisher), 159 Offer: to.StringPtr(offer), 160 Sku: to.StringPtr(sku), 161 Version: to.StringPtr(version), 162 } 163 164 return nil 165 } 166 167 func (s *TemplateBuilder) SetImageUrl(imageUrl string, osType compute.OperatingSystemTypes) error { 168 resource, err := s.getResourceByType(resourceVirtualMachine) 169 if err != nil { 170 return err 171 } 172 173 profile := resource.Properties.StorageProfile 174 profile.OsDisk.OsType = osType 175 profile.OsDisk.Image = &compute.VirtualHardDisk{ 176 URI: to.StringPtr(imageUrl), 177 } 178 179 return nil 180 } 181 182 func (s *TemplateBuilder) SetOSDiskSizeGB(diskSizeGB int32) error { 183 resource, err := s.getResourceByType(resourceVirtualMachine) 184 if err != nil { 185 return err 186 } 187 188 profile := resource.Properties.StorageProfile 189 profile.OsDisk.DiskSizeGB = to.Int32Ptr(diskSizeGB) 190 191 return nil 192 } 193 194 func (s *TemplateBuilder) SetCustomData(customData string) error { 195 resource, err := s.getResourceByType(resourceVirtualMachine) 196 if err != nil { 197 return err 198 } 199 200 profile := resource.Properties.OsProfile 201 profile.CustomData = to.StringPtr(customData) 202 203 return nil 204 } 205 206 func (s *TemplateBuilder) SetVirtualNetwork(virtualNetworkResourceGroup, virtualNetworkName, subnetName string) error { 207 s.setVariable("virtualNetworkResourceGroup", virtualNetworkResourceGroup) 208 s.setVariable("virtualNetworkName", virtualNetworkName) 209 s.setVariable("subnetName", subnetName) 210 211 s.deleteResourceByType(resourceVirtualNetworks) 212 s.deleteResourceByType(resourcePublicIPAddresses) 213 resource, err := s.getResourceByType(resourceNetworkInterfaces) 214 if err != nil { 215 return err 216 } 217 218 s.deleteResourceDependency(resource, func(s string) bool { 219 return strings.Contains(s, "Microsoft.Network/virtualNetworks") || 220 strings.Contains(s, "Microsoft.Network/publicIPAddresses") 221 }) 222 223 (*resource.Properties.IPConfigurations)[0].PublicIPAddress = nil 224 225 return nil 226 } 227 228 func (s *TemplateBuilder) SetPrivateVirtualNetworWithPublicIp(virtualNetworkResourceGroup, virtualNetworkName, subnetName string) error { 229 s.setVariable("virtualNetworkResourceGroup", virtualNetworkResourceGroup) 230 s.setVariable("virtualNetworkName", virtualNetworkName) 231 s.setVariable("subnetName", subnetName) 232 233 s.deleteResourceByType(resourceVirtualNetworks) 234 resource, err := s.getResourceByType(resourceNetworkInterfaces) 235 if err != nil { 236 return err 237 } 238 239 s.deleteResourceDependency(resource, func(s string) bool { 240 return strings.Contains(s, "Microsoft.Network/virtualNetworks") 241 }) 242 243 return nil 244 } 245 246 func (s *TemplateBuilder) SetTags(tags *map[string]*string) error { 247 if tags == nil || len(*tags) == 0 { 248 return nil 249 } 250 251 for i := range *s.template.Resources { 252 (*s.template.Resources)[i].Tags = tags 253 } 254 return nil 255 } 256 257 func (s *TemplateBuilder) ToJSON() (*string, error) { 258 bs, err := json.MarshalIndent(s.template, jsonPrefix, jsonIndent) 259 260 if err != nil { 261 return nil, err 262 } 263 return to.StringPtr(string(bs)), err 264 } 265 266 func (s *TemplateBuilder) getResourceByType(t string) (*Resource, error) { 267 for _, x := range *s.template.Resources { 268 if strings.EqualFold(*x.Type, t) { 269 return &x, nil 270 } 271 } 272 273 return nil, fmt.Errorf("template: could not find a resource of type %s", t) 274 } 275 276 func (s *TemplateBuilder) setVariable(name string, value string) { 277 (*s.template.Variables)[name] = value 278 } 279 280 func (s *TemplateBuilder) toKeyVaultID(name string) string { 281 return s.toResourceID(resourceKeyVaults, name) 282 } 283 284 func (s *TemplateBuilder) toResourceID(id, name string) string { 285 return fmt.Sprintf("[resourceId(resourceGroup().name, '%s', '%s')]", id, name) 286 } 287 288 func (s *TemplateBuilder) toVariable(name string) string { 289 return fmt.Sprintf("[variables('%s')]", name) 290 } 291 292 func (s *TemplateBuilder) deleteResourceByType(resourceType string) { 293 resources := make([]Resource, 0) 294 295 for _, resource := range *s.template.Resources { 296 if *resource.Type == resourceType { 297 continue 298 } 299 resources = append(resources, resource) 300 } 301 302 s.template.Resources = &resources 303 } 304 305 func (s *TemplateBuilder) deleteResourceDependency(resource *Resource, predicate func(string) bool) { 306 deps := make([]string, 0) 307 308 for _, dep := range *resource.DependsOn { 309 if !predicate(dep) { 310 deps = append(deps, dep) 311 } 312 } 313 314 *resource.DependsOn = deps 315 } 316 317 // See https://github.com/Azure/azure-quickstart-templates for a extensive list of templates. 318 319 // Template to deploy a KeyVault. 320 // 321 // This template is still hard-coded unlike the ARM templates used for VMs for 322 // a couple of reasons. 323 // 324 // 1. The SDK defines no types for a Key Vault 325 // 2. The Key Vault template is relatively simple, and is static. 326 // 327 const KeyVault = `{ 328 "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", 329 "contentVersion": "1.0.0.0", 330 "parameters": { 331 "keyVaultName": { 332 "type": "string" 333 }, 334 "keyVaultSecretValue": { 335 "type": "securestring" 336 }, 337 "objectId": { 338 "type": "string" 339 }, 340 "tenantId": { 341 "type": "string" 342 } 343 }, 344 "variables": { 345 "apiVersion": "2015-06-01", 346 "location": "[resourceGroup().location]", 347 "keyVaultSecretName": "packerKeyVaultSecret" 348 }, 349 "resources": [ 350 { 351 "apiVersion": "[variables('apiVersion')]", 352 "type": "Microsoft.KeyVault/vaults", 353 "name": "[parameters('keyVaultName')]", 354 "location": "[variables('location')]", 355 "properties": { 356 "enabledForDeployment": "true", 357 "enabledForTemplateDeployment": "true", 358 "tenantId": "[parameters('tenantId')]", 359 "accessPolicies": [ 360 { 361 "tenantId": "[parameters('tenantId')]", 362 "objectId": "[parameters('objectId')]", 363 "permissions": { 364 "keys": [ "all" ], 365 "secrets": [ "all" ] 366 } 367 } 368 ], 369 "sku": { 370 "name": "standard", 371 "family": "A" 372 } 373 }, 374 "resources": [ 375 { 376 "apiVersion": "[variables('apiVersion')]", 377 "type": "secrets", 378 "name": "[variables('keyVaultSecretName')]", 379 "dependsOn": [ 380 "[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]" 381 ], 382 "properties": { 383 "value": "[parameters('keyVaultSecretValue')]" 384 } 385 } 386 ] 387 } 388 ] 389 }` 390 391 const BasicTemplate = `{ 392 "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", 393 "contentVersion": "1.0.0.0", 394 "parameters": { 395 "adminUsername": { 396 "type": "string" 397 }, 398 "adminPassword": { 399 "type": "string" 400 }, 401 "dnsNameForPublicIP": { 402 "type": "string" 403 }, 404 "osDiskName": { 405 "type": "string" 406 }, 407 "storageAccountBlobEndpoint": { 408 "type": "string" 409 }, 410 "vmSize": { 411 "type": "string" 412 }, 413 "vmName": { 414 "type": "string" 415 } 416 }, 417 "variables": { 418 "addressPrefix": "10.0.0.0/16", 419 "apiVersion": "2017-03-30", 420 "managedDiskApiVersion": "2017-03-30", 421 "networkInterfacesApiVersion": "2017-04-01", 422 "publicIPAddressApiVersion": "2017-04-01", 423 "virtualNetworksApiVersion": "2017-04-01", 424 "location": "[resourceGroup().location]", 425 "nicName": "packerNic", 426 "publicIPAddressName": "packerPublicIP", 427 "publicIPAddressType": "Dynamic", 428 "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", 429 "subnetName": "packerSubnet", 430 "subnetAddressPrefix": "10.0.0.0/24", 431 "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", 432 "virtualNetworkName": "packerNetwork", 433 "virtualNetworkResourceGroup": "[resourceGroup().name]", 434 "vmStorageAccountContainerName": "images", 435 "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" 436 }, 437 "resources": [ 438 { 439 "apiVersion": "[variables('publicIPAddressApiVersion')]", 440 "type": "Microsoft.Network/publicIPAddresses", 441 "name": "[variables('publicIPAddressName')]", 442 "location": "[variables('location')]", 443 "properties": { 444 "publicIPAllocationMethod": "[variables('publicIPAddressType')]", 445 "dnsSettings": { 446 "domainNameLabel": "[parameters('dnsNameForPublicIP')]" 447 } 448 } 449 }, 450 { 451 "apiVersion": "[variables('virtualNetworksApiVersion')]", 452 "type": "Microsoft.Network/virtualNetworks", 453 "name": "[variables('virtualNetworkName')]", 454 "location": "[variables('location')]", 455 "properties": { 456 "addressSpace": { 457 "addressPrefixes": [ 458 "[variables('addressPrefix')]" 459 ] 460 }, 461 "subnets": [ 462 { 463 "name": "[variables('subnetName')]", 464 "properties": { 465 "addressPrefix": "[variables('subnetAddressPrefix')]" 466 } 467 } 468 ] 469 } 470 }, 471 { 472 "apiVersion": "[variables('networkInterfacesApiVersion')]", 473 "type": "Microsoft.Network/networkInterfaces", 474 "name": "[variables('nicName')]", 475 "location": "[variables('location')]", 476 "dependsOn": [ 477 "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]", 478 "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" 479 ], 480 "properties": { 481 "ipConfigurations": [ 482 { 483 "name": "ipconfig", 484 "properties": { 485 "privateIPAllocationMethod": "Dynamic", 486 "publicIPAddress": { 487 "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]" 488 }, 489 "subnet": { 490 "id": "[variables('subnetRef')]" 491 } 492 } 493 } 494 ] 495 } 496 }, 497 { 498 "apiVersion": "[variables('apiVersion')]", 499 "type": "Microsoft.Compute/virtualMachines", 500 "name": "[parameters('vmName')]", 501 "location": "[variables('location')]", 502 "dependsOn": [ 503 "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]" 504 ], 505 "properties": { 506 "hardwareProfile": { 507 "vmSize": "[parameters('vmSize')]" 508 }, 509 "osProfile": { 510 "computerName": "[parameters('vmName')]", 511 "adminUsername": "[parameters('adminUsername')]", 512 "adminPassword": "[parameters('adminPassword')]" 513 }, 514 "storageProfile": { 515 "osDisk": { 516 "name": "osdisk", 517 "vhd": { 518 "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" 519 }, 520 "caching": "ReadWrite", 521 "createOption": "FromImage" 522 } 523 }, 524 "networkProfile": { 525 "networkInterfaces": [ 526 { 527 "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]" 528 } 529 ] 530 }, 531 "diagnosticsProfile": { 532 "bootDiagnostics": { 533 "enabled": false 534 } 535 } 536 } 537 } 538 ] 539 }`