github.com/rahart/packer@v0.12.2-0.20161229105310-282bb6ad370f/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  	resourceNetworkInterfaces = "Microsoft.Network/networkInterfaces"
    18  	resourcePublicIPAddresses = "Microsoft.Network/publicIPAddresses"
    19  	resourceVirtualMachine    = "Microsoft.Compute/virtualMachines"
    20  	resourceVirtualNetworks   = "Microsoft.Network/virtualNetworks"
    21  
    22  	variableSshKeyPath = "sshKeyPath"
    23  )
    24  
    25  type TemplateBuilder struct {
    26  	template *Template
    27  }
    28  
    29  func NewTemplateBuilder(template string) (*TemplateBuilder, error) {
    30  	var t Template
    31  
    32  	err := json.Unmarshal([]byte(template), &t)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  
    37  	return &TemplateBuilder{
    38  		template: &t,
    39  	}, nil
    40  }
    41  
    42  func (s *TemplateBuilder) BuildLinux(sshAuthorizedKey string) error {
    43  	resource, err := s.getResourceByType(resourceVirtualMachine)
    44  	if err != nil {
    45  		return err
    46  	}
    47  
    48  	profile := resource.Properties.OsProfile
    49  	profile.LinuxConfiguration = &compute.LinuxConfiguration{
    50  		SSH: &compute.SSHConfiguration{
    51  			PublicKeys: &[]compute.SSHPublicKey{
    52  				{
    53  					Path:    to.StringPtr(s.toVariable(variableSshKeyPath)),
    54  					KeyData: to.StringPtr(sshAuthorizedKey),
    55  				},
    56  			},
    57  		},
    58  	}
    59  
    60  	return nil
    61  }
    62  
    63  func (s *TemplateBuilder) BuildWindows(keyVaultName, winRMCertificateUrl string) error {
    64  	resource, err := s.getResourceByType(resourceVirtualMachine)
    65  	if err != nil {
    66  		return err
    67  	}
    68  
    69  	profile := resource.Properties.OsProfile
    70  
    71  	profile.Secrets = &[]compute.VaultSecretGroup{
    72  		{
    73  			SourceVault: &compute.SubResource{
    74  				ID: to.StringPtr(s.toResourceID(resourceKeyVaults, keyVaultName)),
    75  			},
    76  			VaultCertificates: &[]compute.VaultCertificate{
    77  				{
    78  					CertificateStore: to.StringPtr("My"),
    79  					CertificateURL:   to.StringPtr(winRMCertificateUrl),
    80  				},
    81  			},
    82  		},
    83  	}
    84  
    85  	profile.WindowsConfiguration = &compute.WindowsConfiguration{
    86  		ProvisionVMAgent: to.BoolPtr(true),
    87  		WinRM: &compute.WinRMConfiguration{
    88  			Listeners: &[]compute.WinRMListener{
    89  				{
    90  					Protocol:       "https",
    91  					CertificateURL: to.StringPtr(winRMCertificateUrl),
    92  				},
    93  			},
    94  		},
    95  	}
    96  	return nil
    97  }
    98  
    99  func (s *TemplateBuilder) SetMarketPlaceImage(publisher, offer, sku, version string) error {
   100  	resource, err := s.getResourceByType(resourceVirtualMachine)
   101  	if err != nil {
   102  		return err
   103  	}
   104  
   105  	profile := resource.Properties.StorageProfile
   106  	profile.ImageReference = &compute.ImageReference{
   107  		Publisher: to.StringPtr(publisher),
   108  		Offer:     to.StringPtr(offer),
   109  		Sku:       to.StringPtr(sku),
   110  		Version:   to.StringPtr(version),
   111  	}
   112  
   113  	return nil
   114  }
   115  
   116  func (s *TemplateBuilder) SetImageUrl(imageUrl string, osType compute.OperatingSystemTypes) error {
   117  	resource, err := s.getResourceByType(resourceVirtualMachine)
   118  	if err != nil {
   119  		return err
   120  	}
   121  
   122  	profile := resource.Properties.StorageProfile
   123  	profile.OsDisk.OsType = osType
   124  	profile.OsDisk.Image = &compute.VirtualHardDisk{
   125  		URI: to.StringPtr(imageUrl),
   126  	}
   127  
   128  	return nil
   129  }
   130  
   131  func (s *TemplateBuilder) SetOSDiskSizeGB(diskSizeGB int32) error {
   132  	resource, err := s.getResourceByType(resourceVirtualMachine)
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	profile := resource.Properties.StorageProfile
   138  	profile.OsDisk.DiskSizeGB = to.Int32Ptr(diskSizeGB)
   139  
   140  	return nil
   141  }
   142  
   143  func (s *TemplateBuilder) SetCustomData(customData string) error {
   144  	resource, err := s.getResourceByType(resourceVirtualMachine)
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	profile := resource.Properties.OsProfile
   150  	profile.CustomData = to.StringPtr(customData)
   151  
   152  	return nil
   153  }
   154  
   155  func (s *TemplateBuilder) SetVirtualNetwork(virtualNetworkResourceGroup, virtualNetworkName, subnetName string) error {
   156  	s.setVariable("virtualNetworkResourceGroup", virtualNetworkResourceGroup)
   157  	s.setVariable("virtualNetworkName", virtualNetworkName)
   158  	s.setVariable("subnetName", subnetName)
   159  
   160  	s.deleteResourceByType(resourceVirtualNetworks)
   161  	s.deleteResourceByType(resourcePublicIPAddresses)
   162  	resource, err := s.getResourceByType(resourceNetworkInterfaces)
   163  	if err != nil {
   164  		return err
   165  	}
   166  
   167  	s.deleteResourceDependency(resource, func(s string) bool {
   168  		return strings.Contains(s, "Microsoft.Network/virtualNetworks") ||
   169  			strings.Contains(s, "Microsoft.Network/publicIPAddresses")
   170  	})
   171  
   172  	(*resource.Properties.IPConfigurations)[0].Properties.PublicIPAddress = nil
   173  
   174  	return nil
   175  }
   176  
   177  func (s *TemplateBuilder) SetTags(tags *map[string]*string) error {
   178  	if tags == nil || len(*tags) == 0 {
   179  		return nil
   180  	}
   181  
   182  	for i := range *s.template.Resources {
   183  		(*s.template.Resources)[i].Tags = tags
   184  	}
   185  	return nil
   186  }
   187  
   188  func (s *TemplateBuilder) ToJSON() (*string, error) {
   189  	bs, err := json.MarshalIndent(s.template, jsonPrefix, jsonIndent)
   190  
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  	return to.StringPtr(string(bs)), err
   195  }
   196  
   197  func (s *TemplateBuilder) getResourceByType(t string) (*Resource, error) {
   198  	for _, x := range *s.template.Resources {
   199  		if strings.EqualFold(*x.Type, t) {
   200  			return &x, nil
   201  		}
   202  	}
   203  
   204  	return nil, fmt.Errorf("template: could not find a resource of type %s", t)
   205  }
   206  
   207  func (s *TemplateBuilder) setVariable(name string, value string) {
   208  	(*s.template.Variables)[name] = value
   209  }
   210  
   211  func (s *TemplateBuilder) toKeyVaultID(name string) string {
   212  	return s.toResourceID(resourceKeyVaults, name)
   213  }
   214  
   215  func (s *TemplateBuilder) toResourceID(id, name string) string {
   216  	return fmt.Sprintf("[resourceId(resourceGroup().name, '%s', '%s')]", id, name)
   217  }
   218  
   219  func (s *TemplateBuilder) toVariable(name string) string {
   220  	return fmt.Sprintf("[variables('%s')]", name)
   221  }
   222  
   223  func (s *TemplateBuilder) deleteResourceByType(resourceType string) {
   224  	resources := make([]Resource, 0)
   225  
   226  	for _, resource := range *s.template.Resources {
   227  		if *resource.Type == resourceType {
   228  			continue
   229  		}
   230  		resources = append(resources, resource)
   231  	}
   232  
   233  	s.template.Resources = &resources
   234  }
   235  
   236  func (s *TemplateBuilder) deleteResourceDependency(resource *Resource, predicate func(string) bool) {
   237  	deps := make([]string, 0)
   238  
   239  	for _, dep := range *resource.DependsOn {
   240  		if !predicate(dep) {
   241  			deps = append(deps, dep)
   242  		}
   243  	}
   244  
   245  	*resource.DependsOn = deps
   246  }
   247  
   248  // See https://github.com/Azure/azure-quickstart-templates for a extensive list of templates.
   249  
   250  // Template to deploy a KeyVault.
   251  //
   252  // This template is still hard-coded unlike the ARM templates used for VMs for
   253  // a couple of reasons.
   254  //
   255  //  1. The SDK defines no types for a Key Vault
   256  //  2. The Key Vault template is relatively simple, and is static.
   257  //
   258  const KeyVault = `{
   259    "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
   260    "contentVersion": "1.0.0.0",
   261    "parameters": {
   262      "keyVaultName": {
   263        "type": "string"
   264      },
   265      "keyVaultSecretValue": {
   266        "type": "securestring"
   267      },
   268      "objectId": {
   269       "type": "string"
   270      },
   271      "tenantId": {
   272        "type": "string"
   273      }
   274    },
   275    "variables": {
   276      "apiVersion": "2015-06-01",
   277      "location": "[resourceGroup().location]",
   278      "keyVaultSecretName": "packerKeyVaultSecret"
   279    },
   280    "resources": [
   281      {
   282        "apiVersion": "[variables('apiVersion')]",
   283        "type": "Microsoft.KeyVault/vaults",
   284        "name": "[parameters('keyVaultName')]",
   285        "location": "[variables('location')]",
   286        "properties": {
   287          "enabledForDeployment": "true",
   288          "enabledForTemplateDeployment": "true",
   289          "tenantId": "[parameters('tenantId')]",
   290          "accessPolicies": [
   291            {
   292              "tenantId": "[parameters('tenantId')]",
   293              "objectId": "[parameters('objectId')]",
   294              "permissions": {
   295                "keys": [ "all" ],
   296                "secrets": [ "all" ]
   297              }
   298            }
   299          ],
   300          "sku": {
   301            "name": "standard",
   302            "family": "A"
   303          }
   304        },
   305        "resources": [
   306          {
   307            "apiVersion": "[variables('apiVersion')]",
   308            "type": "secrets",
   309            "name": "[variables('keyVaultSecretName')]",
   310            "dependsOn": [
   311              "[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]"
   312            ],
   313            "properties": {
   314              "value": "[parameters('keyVaultSecretValue')]"
   315            }
   316          }
   317        ]
   318      }
   319    ]
   320  }`
   321  
   322  const BasicTemplate = `{
   323    "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
   324    "contentVersion": "1.0.0.0",
   325    "parameters": {
   326      "adminUsername": {
   327        "type": "string"
   328      },
   329      "adminPassword": {
   330        "type": "string"
   331      },
   332      "dnsNameForPublicIP": {
   333        "type": "string"
   334      },
   335      "osDiskName": {
   336        "type": "string"
   337      },
   338      "storageAccountBlobEndpoint": {
   339        "type": "string"
   340      },
   341      "vmSize": {
   342        "type": "string"
   343      },
   344      "vmName": {
   345        "type": "string"
   346      }
   347    },
   348    "variables": {
   349      "addressPrefix": "10.0.0.0/16",
   350      "apiVersion": "2015-06-15",
   351      "location": "[resourceGroup().location]",
   352      "nicName": "packerNic",
   353      "publicIPAddressName": "packerPublicIP",
   354      "publicIPAddressType": "Dynamic",
   355      "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
   356      "subnetName": "packerSubnet",
   357      "subnetAddressPrefix": "10.0.0.0/24",
   358      "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
   359      "virtualNetworkName": "packerNetwork",
   360      "virtualNetworkResourceGroup": "[resourceGroup().name]",
   361      "vmStorageAccountContainerName": "images",
   362      "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
   363    },
   364    "resources": [
   365      {
   366        "apiVersion": "[variables('apiVersion')]",
   367        "type": "Microsoft.Network/publicIPAddresses",
   368        "name": "[variables('publicIPAddressName')]",
   369        "location": "[variables('location')]",
   370        "properties": {
   371          "publicIPAllocationMethod": "[variables('publicIPAddressType')]",
   372          "dnsSettings": {
   373            "domainNameLabel": "[parameters('dnsNameForPublicIP')]"
   374          }
   375        }
   376      },
   377      {
   378        "apiVersion": "[variables('apiVersion')]",
   379        "type": "Microsoft.Network/virtualNetworks",
   380        "name": "[variables('virtualNetworkName')]",
   381        "location": "[variables('location')]",
   382        "properties": {
   383          "addressSpace": {
   384            "addressPrefixes": [
   385              "[variables('addressPrefix')]"
   386            ]
   387          },
   388          "subnets": [
   389            {
   390              "name": "[variables('subnetName')]",
   391              "properties": {
   392                "addressPrefix": "[variables('subnetAddressPrefix')]"
   393              }
   394            }
   395          ]
   396        }
   397      },
   398      {
   399        "apiVersion": "[variables('apiVersion')]",
   400        "type": "Microsoft.Network/networkInterfaces",
   401        "name": "[variables('nicName')]",
   402        "location": "[variables('location')]",
   403        "dependsOn": [
   404          "[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
   405          "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
   406        ],
   407        "properties": {
   408          "ipConfigurations": [
   409            {
   410              "name": "ipconfig",
   411              "properties": {
   412                "privateIPAllocationMethod": "Dynamic",
   413                "publicIPAddress": {
   414                  "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
   415                },
   416                "subnet": {
   417                  "id": "[variables('subnetRef')]"
   418                }
   419              }
   420            }
   421          ]
   422        }
   423      },
   424      {
   425        "apiVersion": "[variables('apiVersion')]",
   426        "type": "Microsoft.Compute/virtualMachines",
   427        "name": "[parameters('vmName')]",
   428        "location": "[variables('location')]",
   429        "dependsOn": [
   430          "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
   431        ],
   432        "properties": {
   433          "hardwareProfile": {
   434            "vmSize": "[parameters('vmSize')]"
   435          },
   436          "osProfile": {
   437            "computerName": "[parameters('vmName')]",
   438            "adminUsername": "[parameters('adminUsername')]",
   439            "adminPassword": "[parameters('adminPassword')]"
   440          },
   441          "storageProfile": {
   442            "osDisk": {
   443              "name": "osdisk",
   444              "vhd": {
   445                "uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
   446              },
   447              "caching": "ReadWrite",
   448              "createOption": "FromImage"
   449            }
   450          },
   451          "networkProfile": {
   452            "networkInterfaces": [
   453              {
   454                "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
   455              }
   456            ]
   457          },
   458          "diagnosticsProfile": {
   459            "bootDiagnostics": {
   460               "enabled": false
   461            }
   462          }
   463        }
   464      }
   465    ]
   466  }`