github.com/StackPointCloud/packer@v0.10.2-0.20180716202532-b28098e0f79b/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/services/compute/mgmt/2018-04-01/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.OsType = s.osType 115 profile.OsDisk.CreateOption = compute.DiskCreateOptionTypesFromImage 116 profile.OsDisk.Vhd = nil 117 profile.OsDisk.ManagedDisk = &compute.ManagedDiskParameters{ 118 StorageAccountType: storageAccountType, 119 } 120 121 return nil 122 } 123 124 func (s *TemplateBuilder) SetManagedMarketplaceImage(location, publisher, offer, sku, version, imageID string, storageAccountType compute.StorageAccountTypes) error { 125 resource, err := s.getResourceByType(resourceVirtualMachine) 126 if err != nil { 127 return err 128 } 129 130 profile := resource.Properties.StorageProfile 131 profile.ImageReference = &compute.ImageReference{ 132 Publisher: &publisher, 133 Offer: &offer, 134 Sku: &sku, 135 Version: &version, 136 //ID: &imageID, 137 } 138 profile.OsDisk.OsType = s.osType 139 profile.OsDisk.CreateOption = compute.DiskCreateOptionTypesFromImage 140 profile.OsDisk.Vhd = nil 141 profile.OsDisk.ManagedDisk = &compute.ManagedDiskParameters{ 142 StorageAccountType: storageAccountType, 143 } 144 145 return nil 146 } 147 148 func (s *TemplateBuilder) SetMarketPlaceImage(publisher, offer, sku, version string) error { 149 resource, err := s.getResourceByType(resourceVirtualMachine) 150 if err != nil { 151 return err 152 } 153 154 profile := resource.Properties.StorageProfile 155 profile.ImageReference = &compute.ImageReference{ 156 Publisher: to.StringPtr(publisher), 157 Offer: to.StringPtr(offer), 158 Sku: to.StringPtr(sku), 159 Version: to.StringPtr(version), 160 } 161 162 return nil 163 } 164 165 func (s *TemplateBuilder) SetImageUrl(imageUrl string, osType compute.OperatingSystemTypes) error { 166 resource, err := s.getResourceByType(resourceVirtualMachine) 167 if err != nil { 168 return err 169 } 170 171 profile := resource.Properties.StorageProfile 172 profile.OsDisk.OsType = osType 173 profile.OsDisk.Image = &compute.VirtualHardDisk{ 174 URI: to.StringPtr(imageUrl), 175 } 176 177 return nil 178 } 179 180 func (s *TemplateBuilder) SetPlanInfo(name, product, publisher, promotionCode string) error { 181 var promotionCodeVal *string = nil 182 if promotionCode != "" { 183 promotionCodeVal = to.StringPtr(promotionCode) 184 } 185 186 for i, x := range *s.template.Resources { 187 if strings.EqualFold(*x.Type, resourceVirtualMachine) { 188 (*s.template.Resources)[i].Plan = &Plan{ 189 Name: to.StringPtr(name), 190 Product: to.StringPtr(product), 191 Publisher: to.StringPtr(publisher), 192 PromotionCode: promotionCodeVal, 193 } 194 } 195 } 196 197 return nil 198 } 199 200 func (s *TemplateBuilder) SetOSDiskSizeGB(diskSizeGB int32) error { 201 resource, err := s.getResourceByType(resourceVirtualMachine) 202 if err != nil { 203 return err 204 } 205 206 profile := resource.Properties.StorageProfile 207 profile.OsDisk.DiskSizeGB = to.Int32Ptr(diskSizeGB) 208 209 return nil 210 } 211 212 func (s *TemplateBuilder) SetAdditionalDisks(diskSizeGB []int32, isManaged bool) error { 213 resource, err := s.getResourceByType(resourceVirtualMachine) 214 if err != nil { 215 return err 216 } 217 218 profile := resource.Properties.StorageProfile 219 dataDisks := make([]DataDiskUnion, len(diskSizeGB)) 220 221 for i, additionalsize := range diskSizeGB { 222 dataDisks[i].DiskSizeGB = to.Int32Ptr(additionalsize) 223 dataDisks[i].Lun = to.IntPtr(i) 224 dataDisks[i].Name = to.StringPtr(fmt.Sprintf("datadisk-%d", i+1)) 225 dataDisks[i].CreateOption = "Empty" 226 dataDisks[i].Caching = "ReadWrite" 227 if isManaged { 228 dataDisks[i].Vhd = nil 229 dataDisks[i].ManagedDisk = profile.OsDisk.ManagedDisk 230 } else { 231 dataDisks[i].Vhd = &compute.VirtualHardDisk{ 232 URI: to.StringPtr(fmt.Sprintf("[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/datadisk-', '%d','.vhd')]", i+1)), 233 } 234 dataDisks[i].ManagedDisk = nil 235 } 236 } 237 profile.DataDisks = &dataDisks 238 return nil 239 } 240 241 func (s *TemplateBuilder) SetCustomData(customData string) error { 242 resource, err := s.getResourceByType(resourceVirtualMachine) 243 if err != nil { 244 return err 245 } 246 247 profile := resource.Properties.OsProfile 248 profile.CustomData = to.StringPtr(customData) 249 250 return nil 251 } 252 253 func (s *TemplateBuilder) SetVirtualNetwork(virtualNetworkResourceGroup, virtualNetworkName, subnetName string) error { 254 s.setVariable("virtualNetworkResourceGroup", virtualNetworkResourceGroup) 255 s.setVariable("virtualNetworkName", virtualNetworkName) 256 s.setVariable("subnetName", subnetName) 257 258 s.deleteResourceByType(resourceVirtualNetworks) 259 s.deleteResourceByType(resourcePublicIPAddresses) 260 resource, err := s.getResourceByType(resourceNetworkInterfaces) 261 if err != nil { 262 return err 263 } 264 265 s.deleteResourceDependency(resource, func(s string) bool { 266 return strings.Contains(s, "Microsoft.Network/virtualNetworks") || 267 strings.Contains(s, "Microsoft.Network/publicIPAddresses") 268 }) 269 270 (*resource.Properties.IPConfigurations)[0].PublicIPAddress = nil 271 272 return nil 273 } 274 275 func (s *TemplateBuilder) SetPrivateVirtualNetworkWithPublicIp(virtualNetworkResourceGroup, virtualNetworkName, subnetName string) error { 276 s.setVariable("virtualNetworkResourceGroup", virtualNetworkResourceGroup) 277 s.setVariable("virtualNetworkName", virtualNetworkName) 278 s.setVariable("subnetName", subnetName) 279 280 s.deleteResourceByType(resourceVirtualNetworks) 281 resource, err := s.getResourceByType(resourceNetworkInterfaces) 282 if err != nil { 283 return err 284 } 285 286 s.deleteResourceDependency(resource, func(s string) bool { 287 return strings.Contains(s, "Microsoft.Network/virtualNetworks") 288 }) 289 290 return nil 291 } 292 293 func (s *TemplateBuilder) SetTags(tags *map[string]*string) error { 294 if tags == nil || len(*tags) == 0 { 295 return nil 296 } 297 298 for i := range *s.template.Resources { 299 (*s.template.Resources)[i].Tags = tags 300 } 301 return nil 302 } 303 304 func (s *TemplateBuilder) ToJSON() (*string, error) { 305 bs, err := json.MarshalIndent(s.template, jsonPrefix, jsonIndent) 306 307 if err != nil { 308 return nil, err 309 } 310 return to.StringPtr(string(bs)), err 311 } 312 313 func (s *TemplateBuilder) getResourceByType(t string) (*Resource, error) { 314 for _, x := range *s.template.Resources { 315 if strings.EqualFold(*x.Type, t) { 316 return &x, nil 317 } 318 } 319 320 return nil, fmt.Errorf("template: could not find a resource of type %s", t) 321 } 322 323 func (s *TemplateBuilder) getResourceByType2(t string) (**Resource, error) { 324 for _, x := range *s.template.Resources { 325 if strings.EqualFold(*x.Type, t) { 326 p := &x 327 return &p, nil 328 } 329 } 330 331 return nil, fmt.Errorf("template: could not find a resource of type %s", t) 332 } 333 334 func (s *TemplateBuilder) setVariable(name string, value string) { 335 (*s.template.Variables)[name] = value 336 } 337 338 func (s *TemplateBuilder) toKeyVaultID(name string) string { 339 return s.toResourceID(resourceKeyVaults, name) 340 } 341 342 func (s *TemplateBuilder) toResourceID(id, name string) string { 343 return fmt.Sprintf("[resourceId(resourceGroup().name, '%s', '%s')]", id, name) 344 } 345 346 func (s *TemplateBuilder) toVariable(name string) string { 347 return fmt.Sprintf("[variables('%s')]", name) 348 } 349 350 func (s *TemplateBuilder) deleteResourceByType(resourceType string) { 351 resources := make([]Resource, 0) 352 353 for _, resource := range *s.template.Resources { 354 if *resource.Type == resourceType { 355 continue 356 } 357 resources = append(resources, resource) 358 } 359 360 s.template.Resources = &resources 361 } 362 363 func (s *TemplateBuilder) deleteResourceDependency(resource *Resource, predicate func(string) bool) { 364 deps := make([]string, 0) 365 366 for _, dep := range *resource.DependsOn { 367 if !predicate(dep) { 368 deps = append(deps, dep) 369 } 370 } 371 372 *resource.DependsOn = deps 373 } 374 375 // See https://github.com/Azure/azure-quickstart-templates for a extensive list of templates. 376 377 // Template to deploy a KeyVault. 378 // 379 // This template is still hard-coded unlike the ARM templates used for VMs for 380 // a couple of reasons. 381 // 382 // 1. The SDK defines no types for a Key Vault 383 // 2. The Key Vault template is relatively simple, and is static. 384 // 385 const KeyVault = `{ 386 "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", 387 "contentVersion": "1.0.0.0", 388 "parameters": { 389 "keyVaultName": { 390 "type": "string" 391 }, 392 "keyVaultSecretValue": { 393 "type": "securestring" 394 }, 395 "objectId": { 396 "type": "string" 397 }, 398 "tenantId": { 399 "type": "string" 400 } 401 }, 402 "variables": { 403 "apiVersion": "2015-06-01", 404 "location": "[resourceGroup().location]", 405 "keyVaultSecretName": "packerKeyVaultSecret" 406 }, 407 "resources": [ 408 { 409 "apiVersion": "[variables('apiVersion')]", 410 "type": "Microsoft.KeyVault/vaults", 411 "name": "[parameters('keyVaultName')]", 412 "location": "[variables('location')]", 413 "properties": { 414 "enabledForDeployment": "true", 415 "enabledForTemplateDeployment": "true", 416 "tenantId": "[parameters('tenantId')]", 417 "accessPolicies": [ 418 { 419 "tenantId": "[parameters('tenantId')]", 420 "objectId": "[parameters('objectId')]", 421 "permissions": { 422 "keys": [ "all" ], 423 "secrets": [ "all" ] 424 } 425 } 426 ], 427 "sku": { 428 "name": "standard", 429 "family": "A" 430 } 431 }, 432 "resources": [ 433 { 434 "apiVersion": "[variables('apiVersion')]", 435 "type": "secrets", 436 "name": "[variables('keyVaultSecretName')]", 437 "dependsOn": [ 438 "[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]" 439 ], 440 "properties": { 441 "value": "[parameters('keyVaultSecretValue')]" 442 } 443 } 444 ] 445 } 446 ] 447 }` 448 449 const BasicTemplate = `{ 450 "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", 451 "contentVersion": "1.0.0.0", 452 "parameters": { 453 "adminUsername": { 454 "type": "string" 455 }, 456 "adminPassword": { 457 "type": "string" 458 }, 459 "dnsNameForPublicIP": { 460 "type": "string" 461 }, 462 "nicName": { 463 "type": "string" 464 }, 465 "osDiskName": { 466 "type": "string" 467 }, 468 "publicIPAddressName": { 469 "type": "string" 470 }, 471 "subnetName": { 472 "type": "string" 473 }, 474 "storageAccountBlobEndpoint": { 475 "type": "string" 476 }, 477 "virtualNetworkName": { 478 "type": "string" 479 }, 480 "vmSize": { 481 "type": "string" 482 }, 483 "vmName": { 484 "type": "string" 485 } 486 }, 487 "variables": { 488 "addressPrefix": "10.0.0.0/16", 489 "apiVersion": "2017-03-30", 490 "managedDiskApiVersion": "2017-03-30", 491 "networkInterfacesApiVersion": "2017-04-01", 492 "publicIPAddressApiVersion": "2017-04-01", 493 "virtualNetworksApiVersion": "2017-04-01", 494 "location": "[resourceGroup().location]", 495 "publicIPAddressType": "Dynamic", 496 "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", 497 "subnetName": "[parameters('subnetName')]", 498 "subnetAddressPrefix": "10.0.0.0/24", 499 "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", 500 "virtualNetworkName": "[parameters('virtualNetworkName')]", 501 "virtualNetworkResourceGroup": "[resourceGroup().name]", 502 "vmStorageAccountContainerName": "images", 503 "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" 504 }, 505 "resources": [ 506 { 507 "apiVersion": "[variables('publicIPAddressApiVersion')]", 508 "type": "Microsoft.Network/publicIPAddresses", 509 "name": "[parameters('publicIPAddressName')]", 510 "location": "[variables('location')]", 511 "properties": { 512 "publicIPAllocationMethod": "[variables('publicIPAddressType')]", 513 "dnsSettings": { 514 "domainNameLabel": "[parameters('dnsNameForPublicIP')]" 515 } 516 } 517 }, 518 { 519 "apiVersion": "[variables('virtualNetworksApiVersion')]", 520 "type": "Microsoft.Network/virtualNetworks", 521 "name": "[variables('virtualNetworkName')]", 522 "location": "[variables('location')]", 523 "properties": { 524 "addressSpace": { 525 "addressPrefixes": [ 526 "[variables('addressPrefix')]" 527 ] 528 }, 529 "subnets": [ 530 { 531 "name": "[variables('subnetName')]", 532 "properties": { 533 "addressPrefix": "[variables('subnetAddressPrefix')]" 534 } 535 } 536 ] 537 } 538 }, 539 { 540 "apiVersion": "[variables('networkInterfacesApiVersion')]", 541 "type": "Microsoft.Network/networkInterfaces", 542 "name": "[parameters('nicName')]", 543 "location": "[variables('location')]", 544 "dependsOn": [ 545 "[concat('Microsoft.Network/publicIPAddresses/', parameters('publicIPAddressName'))]", 546 "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" 547 ], 548 "properties": { 549 "ipConfigurations": [ 550 { 551 "name": "ipconfig", 552 "properties": { 553 "privateIPAllocationMethod": "Dynamic", 554 "publicIPAddress": { 555 "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" 556 }, 557 "subnet": { 558 "id": "[variables('subnetRef')]" 559 } 560 } 561 } 562 ] 563 } 564 }, 565 { 566 "apiVersion": "[variables('apiVersion')]", 567 "type": "Microsoft.Compute/virtualMachines", 568 "name": "[parameters('vmName')]", 569 "location": "[variables('location')]", 570 "dependsOn": [ 571 "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" 572 ], 573 "properties": { 574 "hardwareProfile": { 575 "vmSize": "[parameters('vmSize')]" 576 }, 577 "osProfile": { 578 "computerName": "[parameters('vmName')]", 579 "adminUsername": "[parameters('adminUsername')]", 580 "adminPassword": "[parameters('adminPassword')]" 581 }, 582 "storageProfile": { 583 "osDisk": { 584 "name": "[parameters('osDiskName')]", 585 "vhd": { 586 "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]" 587 }, 588 "caching": "ReadWrite", 589 "createOption": "FromImage" 590 } 591 }, 592 "networkProfile": { 593 "networkInterfaces": [ 594 { 595 "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" 596 } 597 ] 598 }, 599 "diagnosticsProfile": { 600 "bootDiagnostics": { 601 "enabled": false 602 } 603 } 604 } 605 } 606 ] 607 }`