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