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  }`