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