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