github.com/mitchellh/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  }`