Implement support for custom images.

This commit is contained in:
Christopher Boumenot 2016-05-20 23:01:16 -07:00
parent 873760e69e
commit 5950d3d92b
32 changed files with 1939 additions and 839 deletions

View File

@ -0,0 +1,67 @@
{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"parameters": {
"keyVaultName": {
"type": "string"
},
"keyVaultSecretValue": {
"type": "securestring"
},
"objectId": {
"type": "string"
},
"tenantId": {
"type": "string"
}
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"location": "[variables('location')]",
"name": "[parameters('keyVaultName')]",
"properties": {
"accessPolicies": [
{
"objectId": "[parameters('objectId')]",
"permissions": {
"keys": [
"all"
],
"secrets": [
"all"
]
},
"tenantId": "[parameters('tenantId')]"
}
],
"enabledForDeployment": "true",
"enabledForTemplateDeployment": "true",
"sku": {
"family": "A",
"name": "standard"
},
"tenantId": "[parameters('tenantId')]"
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"dependsOn": [
"[concat('Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]"
],
"name": "[variables('keyVaultSecretName')]",
"properties": {
"value": "[parameters('keyVaultSecretValue')]"
},
"type": "secrets"
}
],
"type": "Microsoft.KeyVault/vaults"
}
],
"variables": {
"apiVersion": "2015-06-01",
"keyVaultSecretName": "packerKeyVaultSecret",
"location": "[resourceGroup().location]"
}
}

View File

@ -0,0 +1,160 @@
{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"parameters": {
"adminPassword": {
"type": "string"
},
"adminUsername": {
"type": "string"
},
"dnsNameForPublicIP": {
"type": "string"
},
"osDiskName": {
"type": "string"
},
"storageAccountBlobEndpoint": {
"type": "string"
},
"vmName": {
"type": "string"
},
"vmSize": {
"type": "string"
}
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"location": "[variables('location')]",
"name": "[variables('publicIPAddressName')]",
"properties": {
"dnsSettings": {
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
},
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
},
"type": "Microsoft.Network/publicIPAddresses"
},
{
"apiVersion": "[variables('apiVersion')]",
"location": "[variables('location')]",
"name": "[variables('virtualNetworkName')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"subnets": [
{
"name": "[variables('subnetName')]",
"properties": {
"addressPrefix": "[variables('subnetAddressPrefix')]"
}
}
]
},
"type": "Microsoft.Network/virtualNetworks"
},
{
"apiVersion": "[variables('apiVersion')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
],
"location": "[variables('location')]",
"name": "[variables('nicName')]",
"properties": {
"ipConfigurations": [
{
"name": "ipconfig",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
},
"subnet": {
"id": "[variables('subnetRef')]"
}
}
}
]
},
"type": "Microsoft.Network/networkInterfaces"
},
{
"apiVersion": "[variables('apiVersion')]",
"dependsOn": [
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
],
"location": "[variables('location')]",
"name": "[parameters('vmName')]",
"properties": {
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": false
}
},
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
}
]
},
"osProfile": {
"adminPassword": "[parameters('adminPassword')]",
"adminUsername": "[parameters('adminUsername')]",
"computerName": "[parameters('vmName')]",
"linuxConfiguration": {
"ssh": {
"publicKeys": [
{
"keyData": "",
"path": "[variables('sshKeyPath')]"
}
]
}
}
},
"storageProfile": {
"imageReference": {
"offer": "ImageOffer",
"publisher": "ImagePublisher",
"sku": "ImageSku",
"version": "ImageVersion"
},
"osDisk": {
"caching": "ReadWrite",
"createOption": "FromImage",
"name": "osdisk",
"vhd": {
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
}
}
}
},
"type": "Microsoft.Compute/virtualMachines"
}
],
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2015-06-15",
"location": "[resourceGroup().location]",
"nicName": "packerNic",
"publicIPAddressName": "packerPublicIP",
"publicIPAddressType": "Dynamic",
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
"subnetAddressPrefix": "10.0.0.0/24",
"subnetName": "packerSubnet",
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"virtualNetworkName": "packerNetwork",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
}
}

View File

@ -0,0 +1,158 @@
{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"parameters": {
"adminPassword": {
"type": "string"
},
"adminUsername": {
"type": "string"
},
"dnsNameForPublicIP": {
"type": "string"
},
"osDiskName": {
"type": "string"
},
"storageAccountBlobEndpoint": {
"type": "string"
},
"vmName": {
"type": "string"
},
"vmSize": {
"type": "string"
}
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"location": "[variables('location')]",
"name": "[variables('publicIPAddressName')]",
"properties": {
"dnsSettings": {
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
},
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
},
"type": "Microsoft.Network/publicIPAddresses"
},
{
"apiVersion": "[variables('apiVersion')]",
"location": "[variables('location')]",
"name": "[variables('virtualNetworkName')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"subnets": [
{
"name": "[variables('subnetName')]",
"properties": {
"addressPrefix": "[variables('subnetAddressPrefix')]"
}
}
]
},
"type": "Microsoft.Network/virtualNetworks"
},
{
"apiVersion": "[variables('apiVersion')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
],
"location": "[variables('location')]",
"name": "[variables('nicName')]",
"properties": {
"ipConfigurations": [
{
"name": "ipconfig",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
},
"subnet": {
"id": "[variables('subnetRef')]"
}
}
}
]
},
"type": "Microsoft.Network/networkInterfaces"
},
{
"apiVersion": "[variables('apiVersion')]",
"dependsOn": [
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
],
"location": "[variables('location')]",
"name": "[parameters('vmName')]",
"properties": {
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": false
}
},
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
}
]
},
"osProfile": {
"adminPassword": "[parameters('adminPassword')]",
"adminUsername": "[parameters('adminUsername')]",
"computerName": "[parameters('vmName')]",
"linuxConfiguration": {
"ssh": {
"publicKeys": [
{
"keyData": "",
"path": "[variables('sshKeyPath')]"
}
]
}
}
},
"storageProfile": {
"osDisk": {
"caching": "ReadWrite",
"createOption": "FromImage",
"image": {
"uri": "https://localhost/custom.vhd"
},
"name": "osdisk",
"osType": "Linux",
"vhd": {
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
}
}
}
},
"type": "Microsoft.Compute/virtualMachines"
}
],
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2015-06-15",
"location": "[resourceGroup().location]",
"nicName": "packerNic",
"publicIPAddressName": "packerPublicIP",
"publicIPAddressType": "Dynamic",
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
"subnetAddressPrefix": "10.0.0.0/24",
"subnetName": "packerSubnet",
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"virtualNetworkName": "packerNetwork",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
}
}

View File

@ -20,6 +20,7 @@ import (
packerCommon "github.com/mitchellh/packer/common" packerCommon "github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/helper/communicator" "github.com/mitchellh/packer/helper/communicator"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"strings"
) )
type Builder struct { type Builder struct {
@ -82,14 +83,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
} }
b.setTemplateParameters(b.stateBag) b.setTemplateParameters(b.stateBag)
var steps []multistep.Step var steps []multistep.Step
if b.config.OSType == constants.Target_Linux { if strings.EqualFold(b.config.OSType, constants.Target_Linux) {
steps = []multistep.Step{ steps = []multistep.Step{
NewStepCreateResourceGroup(azureClient, ui), NewStepCreateResourceGroup(azureClient, ui),
NewStepValidateTemplate(azureClient, ui, Linux), NewStepValidateTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment),
NewStepDeployTemplate(azureClient, ui, Linux), NewStepDeployTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment),
NewStepGetIPAddress(azureClient, ui), NewStepGetIPAddress(azureClient, ui),
&communicator.StepConnectSSH{ &communicator.StepConnectSSH{
Config: &b.config.Comm, Config: &b.config.Comm,
@ -103,15 +103,15 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
NewStepDeleteResourceGroup(azureClient, ui), NewStepDeleteResourceGroup(azureClient, ui),
NewStepDeleteOSDisk(azureClient, ui), NewStepDeleteOSDisk(azureClient, ui),
} }
} else if b.config.OSType == constants.Target_Windows { } else if strings.EqualFold(b.config.OSType, constants.Target_Windows) {
steps = []multistep.Step{ steps = []multistep.Step{
NewStepCreateResourceGroup(azureClient, ui), NewStepCreateResourceGroup(azureClient, ui),
NewStepValidateTemplate(azureClient, ui, KeyVault), NewStepValidateTemplate(azureClient, ui, b.config, GetKeyVaultDeployment),
NewStepDeployTemplate(azureClient, ui, KeyVault), NewStepDeployTemplate(azureClient, ui, b.config, GetKeyVaultDeployment),
NewStepGetCertificate(azureClient, ui), NewStepGetCertificate(azureClient, ui),
NewStepSetCertificate(b.config, ui), NewStepSetCertificate(b.config, ui),
NewStepValidateTemplate(azureClient, ui, Windows), NewStepValidateTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment),
NewStepDeployTemplate(azureClient, ui, Windows), NewStepDeployTemplate(azureClient, ui, b.config, GetVirtualMachineDeployment),
NewStepGetIPAddress(azureClient, ui), NewStepGetIPAddress(azureClient, ui),
&communicator.StepConnectWinRM{ &communicator.StepConnectWinRM{
Config: &b.config.Comm, Config: &b.config.Comm,
@ -214,7 +214,6 @@ func (b *Builder) configureStateBag(stateBag multistep.StateBag) {
} }
func (b *Builder) setTemplateParameters(stateBag multistep.StateBag) { func (b *Builder) setTemplateParameters(stateBag multistep.StateBag) {
stateBag.Put(constants.ArmTemplateParameters, b.config.toTemplateParameters())
stateBag.Put(constants.ArmVirtualMachineCaptureParameters, b.config.toVirtualMachineCaptureParameters()) stateBag.Put(constants.ArmVirtualMachineCaptureParameters, b.config.toVirtualMachineCaptureParameters())
} }

View File

@ -24,7 +24,6 @@ func TestStateBagShouldBePopulatedExpectedValues(t *testing.T) {
constants.ArmLocation, constants.ArmLocation,
constants.ArmResourceGroupName, constants.ArmResourceGroupName,
constants.ArmStorageAccountName, constants.ArmStorageAccountName,
constants.ArmTemplateParameters,
constants.ArmVirtualMachineCaptureParameters, constants.ArmVirtualMachineCaptureParameters,
constants.ArmPublicIPAddressName, constants.ArmPublicIPAddressName,
} }

View File

@ -65,6 +65,7 @@ type Config struct {
ImageOffer string `mapstructure:"image_offer"` ImageOffer string `mapstructure:"image_offer"`
ImageSku string `mapstructure:"image_sku"` ImageSku string `mapstructure:"image_sku"`
ImageVersion string `mapstructure:"image_version"` ImageVersion string `mapstructure:"image_version"`
ImageUrl string `mapstructure:"image_url"`
Location string `mapstructure:"location"` Location string `mapstructure:"location"`
VMSize string `mapstructure:"vm_size"` VMSize string `mapstructure:"vm_size"`
@ -109,38 +110,6 @@ type keyVaultCertificate struct {
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`
} }
// If we ever feel the need to support more templates consider moving this
// method to its own factory class.
func (c *Config) toTemplateParameters() *TemplateParameters {
templateParameters := &TemplateParameters{
AdminUsername: &TemplateParameter{c.UserName},
AdminPassword: &TemplateParameter{c.Password},
DnsNameForPublicIP: &TemplateParameter{c.tmpComputeName},
ImageOffer: &TemplateParameter{c.ImageOffer},
ImagePublisher: &TemplateParameter{c.ImagePublisher},
ImageSku: &TemplateParameter{c.ImageSku},
ImageVersion: &TemplateParameter{c.ImageVersion},
OSDiskName: &TemplateParameter{c.tmpOSDiskName},
StorageAccountBlobEndpoint: &TemplateParameter{c.storageAccountBlobEndpoint},
VMSize: &TemplateParameter{c.VMSize},
VMName: &TemplateParameter{c.tmpComputeName},
}
switch c.OSType {
case constants.Target_Linux:
templateParameters.SshAuthorizedKey = &TemplateParameter{c.sshAuthorizedKey}
case constants.Target_Windows:
templateParameters.TenantId = &TemplateParameter{c.TenantID}
templateParameters.ObjectId = &TemplateParameter{c.ObjectID}
templateParameters.KeyVaultName = &TemplateParameter{c.tmpKeyVaultName}
templateParameters.KeyVaultSecretValue = &TemplateParameter{c.winrmCertificate}
templateParameters.WinRMCertificateUrl = &TemplateParameter{c.tmpWinRMCertificateUrl}
}
return templateParameters
}
func (c *Config) toVirtualMachineCaptureParameters() *compute.VirtualMachineCaptureParameters { func (c *Config) toVirtualMachineCaptureParameters() *compute.VirtualMachineCaptureParameters {
return &compute.VirtualMachineCaptureParameters{ return &compute.VirtualMachineCaptureParameters{
DestinationContainerName: &c.CaptureContainerName, DestinationContainerName: &c.CaptureContainerName,
@ -231,7 +200,7 @@ func newConfig(raws ...interface{}) (*Config, []string, error) {
// NOTE: if the user did not specify a communicator, then default to both // NOTE: if the user did not specify a communicator, then default to both
// SSH and WinRM. This is for backwards compatibility because the code did // SSH and WinRM. This is for backwards compatibility because the code did
// not specifically force the user to specify a value. // not specifically force the user to set a communicator.
if c.Comm.Type == "" || strings.EqualFold(c.Comm.Type, "ssh") { if c.Comm.Type == "" || strings.EqualFold(c.Comm.Type, "ssh") {
err = setSshValues(&c) err = setSshValues(&c)
if err != nil { if err != nil {
@ -350,7 +319,7 @@ func provideDefaultValues(c *Config) {
c.VMSize = DefaultVMSize c.VMSize = DefaultVMSize
} }
if c.ImageVersion == "" { if c.ImageUrl == "" && c.ImageVersion == "" {
c.ImageVersion = DefaultImageVersion c.ImageVersion = DefaultImageVersion
} }
@ -434,16 +403,22 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
///////////////////////////////////////////// /////////////////////////////////////////////
// Compute // Compute
if c.ImagePublisher == "" { if c.ImageUrl == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("A image_publisher must be specified")) if c.ImagePublisher == "" {
} errs = packer.MultiErrorAppend(errs, fmt.Errorf("An image_publisher must be specified"))
}
if c.ImageOffer == "" { if c.ImageOffer == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("A image_offer must be specified")) errs = packer.MultiErrorAppend(errs, fmt.Errorf("An image_offer must be specified"))
} }
if c.ImageSku == "" { if c.ImageSku == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("A image_sku must be specified")) errs = packer.MultiErrorAppend(errs, fmt.Errorf("An image_sku must be specified"))
}
} else {
if c.ImagePublisher != "" || c.ImageOffer != "" || c.ImageSku != "" || c.ImageVersion != "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("An image_url must not be specified if image_publisher, image_offer, image_sku, or image_version is specified"))
}
} }
if c.Location == "" { if c.Location == "" {

View File

@ -100,6 +100,47 @@ func TestConfigShouldDefaultImageVersionToLatest(t *testing.T) {
} }
} }
func TestConfigShouldNotDefaultImageVersionIfCustomImage(t *testing.T) {
config := map[string]string{
"capture_name_prefix": "ignore",
"capture_container_name": "ignore",
"location": "ignore",
"image_url": "ignore",
"storage_account": "ignore",
"subscription_id": "ignore",
"os_type": constants.Target_Linux,
"communicator": "none",
}
c, _, _ := newConfig(config, getPackerConfiguration())
if c.ImageVersion != "" {
t.Errorf("Expected 'ImageVersion' to empty, but got '%s'.", c.ImageVersion)
}
}
func TestConfigShouldRejectCustomImageAndMarketPlace(t *testing.T) {
config := map[string]string{
"capture_name_prefix": "ignore",
"capture_container_name": "ignore",
"location": "ignore",
"image_url": "ignore",
"storage_account": "ignore",
"subscription_id": "ignore",
"os_type": constants.Target_Linux,
"communicator": "none",
}
packerConfiguration := getPackerConfiguration()
marketPlace := []string{"image_publisher", "image_offer", "image_sku"}
for _, x := range marketPlace {
config[x] = "ignore"
_, _, err := newConfig(config, packerConfiguration)
if err == nil {
t.Errorf("Expected Config to reject image_url and %s, but it did not", x)
}
}
}
func TestConfigShouldDefaultToPublicCloud(t *testing.T) { func TestConfigShouldDefaultToPublicCloud(t *testing.T) {
c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())
@ -208,91 +249,6 @@ func TestSystemShouldDefineRuntimeValues(t *testing.T) {
} }
} }
func TestConfigShouldTransformToTemplateParameters(t *testing.T) {
c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())
templateParameters := c.toTemplateParameters()
if templateParameters.AdminUsername.Value != c.UserName {
t.Errorf("Expected AdminUsername to be equal to config's AdminUsername, but they were '%s' and '%s' respectively.", templateParameters.AdminUsername.Value, c.UserName)
}
if templateParameters.DnsNameForPublicIP.Value != c.tmpComputeName {
t.Errorf("Expected DnsNameForPublicIP to be equal to config's DnsNameForPublicIP, but they were '%s' and '%s' respectively.", templateParameters.DnsNameForPublicIP.Value, c.tmpComputeName)
}
if templateParameters.ImageOffer.Value != c.ImageOffer {
t.Errorf("Expected ImageOffer to be equal to config's ImageOffer, but they were '%s' and '%s' respectively.", templateParameters.ImageOffer.Value, c.ImageOffer)
}
if templateParameters.ImagePublisher.Value != c.ImagePublisher {
t.Errorf("Expected ImagePublisher to be equal to config's ImagePublisher, but they were '%s' and '%s' respectively.", templateParameters.ImagePublisher.Value, c.ImagePublisher)
}
if templateParameters.ImageSku.Value != c.ImageSku {
t.Errorf("Expected ImageSku to be equal to config's ImageSku, but they were '%s' and '%s' respectively.", templateParameters.ImageSku.Value, c.ImageSku)
}
if templateParameters.OSDiskName.Value != c.tmpOSDiskName {
t.Errorf("Expected OSDiskName to be equal to config's OSDiskName, but they were '%s' and '%s' respectively.", templateParameters.OSDiskName.Value, c.tmpOSDiskName)
}
if templateParameters.StorageAccountBlobEndpoint.Value != c.storageAccountBlobEndpoint {
t.Errorf("Expected StorageAccountBlobEndpoint to be equal to config's storageAccountBlobEndpoint, but they were '%s' and '%s' respectively.", templateParameters.StorageAccountBlobEndpoint.Value, c.storageAccountBlobEndpoint)
}
if templateParameters.VMName.Value != c.tmpComputeName {
t.Errorf("Expected VMName to be equal to config's VMName, but they were '%s' and '%s' respectively.", templateParameters.VMName.Value, c.tmpComputeName)
}
if templateParameters.VMSize.Value != c.VMSize {
t.Errorf("Expected VMSize to be equal to config's VMSize, but they were '%s' and '%s' respectively.", templateParameters.VMSize.Value, c.VMSize)
}
}
func TestConfigShouldTransformToTemplateParametersLinux(t *testing.T) {
c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())
c.OSType = constants.Target_Linux
templateParameters := c.toTemplateParameters()
if templateParameters.KeyVaultSecretValue != nil {
t.Errorf("Expected KeyVaultSecretValue to be empty for an os_type == '%s', but it was not.", c.OSType)
}
if templateParameters.ObjectId != nil {
t.Errorf("Expected ObjectId to be empty for an os_type == '%s', but it was not.", c.OSType)
}
if templateParameters.TenantId != nil {
t.Errorf("Expected TenantId to be empty for an os_type == '%s', but it was not.", c.OSType)
}
}
func TestConfigShouldTransformToTemplateParametersWindows(t *testing.T) {
c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())
c.OSType = constants.Target_Windows
templateParameters := c.toTemplateParameters()
if templateParameters.SshAuthorizedKey != nil {
t.Errorf("Expected SshAuthorizedKey to be empty for an os_type == '%s', but it was not.", c.OSType)
}
if templateParameters.KeyVaultName == nil {
t.Errorf("Expected KeyVaultName to not be empty for an os_type == '%s', but it was not.", c.OSType)
}
if templateParameters.KeyVaultSecretValue == nil {
t.Errorf("Expected KeyVaultSecretValue to not be empty for an os_type == '%s', but it was not.", c.OSType)
}
if templateParameters.ObjectId == nil {
t.Errorf("Expected ObjectId to not be empty for an os_type == '%s', but it was not.", c.OSType)
}
if templateParameters.TenantId == nil {
t.Errorf("Expected TenantId to not be empty for an os_type == '%s', but it was not.", c.OSType)
}
}
func TestConfigShouldTransformToVirtualMachineCaptureParameters(t *testing.T) { func TestConfigShouldTransformToVirtualMachineCaptureParameters(t *testing.T) {
c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration()) c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())
parameters := c.toVirtualMachineCaptureParameters() parameters := c.toVirtualMachineCaptureParameters()
@ -508,12 +464,26 @@ func getArmBuilderConfiguration() map[string]string {
return m return m
} }
func getArmBuilderConfigurationWithWindows() map[string]string {
m := make(map[string]string)
for _, v := range requiredConfigValues {
m[v] = fmt.Sprintf("ignored00")
}
m["object_id"] = "ignored00"
m["tenant_id"] = "ignored00"
m["winrm_username"] = "ignored00"
m["communicator"] = "winrm"
m["os_type"] = constants.Target_Windows
return m
}
func getPackerConfiguration() interface{} { func getPackerConfiguration() interface{} {
config := map[string]interface{}{ config := map[string]interface{}{
"packer_build_name": "azure-arm-vm", "packer_build_name": "azure-arm-vm",
"packer_builder_type": "azure-arm-vm", "packer_builder_type": "azure-arm-vm",
"packer_debug": "false", "packer_debug": "false",
"packer_force": "false", "packer_force": "false",
"packer_template_path": "/home/jenkins/azure-arm-vm/template.json", "packer_template_path": "/home/jenkins/azure-arm-vm/template.json",
} }

View File

@ -1,66 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
package arm
import (
"encoding/json"
"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
)
type DeploymentFactory struct {
template string
}
func newDeploymentFactory(template string) DeploymentFactory {
return DeploymentFactory{
template: template,
}
}
func (f *DeploymentFactory) create(templateParameters TemplateParameters) (*resources.Deployment, error) {
template, err := f.getTemplate(templateParameters)
if err != nil {
return nil, err
}
parameters, err := f.getTemplateParameters(templateParameters)
if err != nil {
return nil, err
}
return &resources.Deployment{
Properties: &resources.DeploymentProperties{
Mode: resources.Incremental,
Template: template,
Parameters: parameters,
},
}, nil
}
func (f *DeploymentFactory) getTemplate(templateParameters TemplateParameters) (*map[string]interface{}, error) {
var t map[string]interface{}
err := json.Unmarshal([]byte(f.template), &t)
if err != nil {
return nil, err
}
return &t, nil
}
func (f *DeploymentFactory) getTemplateParameters(templateParameters TemplateParameters) (*map[string]interface{}, error) {
b, err := json.Marshal(templateParameters)
if err != nil {
return nil, err
}
var t map[string]interface{}
err = json.Unmarshal(b, &t)
if err != nil {
return nil, err
}
return &t, nil
}

View File

@ -1,92 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
package arm
import (
"testing"
)
func TestDeploymentFactoryShouldBeIncremental(t *testing.T) {
var testSubject = newDeploymentFactory(Linux)
deployment, err := testSubject.create(getTemplateParameters())
if err != nil {
t.Fatalf("ERROR: %s\n", err)
}
if deployment.Properties.Mode != "Incremental" {
t.Fatalf("Expected the mode to be 'Incremental', but got '%s'.", deployment.Properties.Mode)
}
}
// Either {Template,Parameter} are set or {Template,Parameter}Link values are
// set, but never both.
func TestDeploymentFactoryShouldNotSetLinks(t *testing.T) {
testSubject := newDeploymentFactory(Linux)
deployment, err := testSubject.create(getTemplateParameters())
if err != nil {
t.Fatalf("ERROR: %s\n", err)
}
if deployment.Properties.ParametersLink != nil {
t.Fatalf("Expected the ParametersLink to be nil!")
}
if deployment.Properties.TemplateLink != nil {
t.Fatalf("Expected the TemplateLink to be nil!")
}
if deployment.Properties.Parameters == nil {
t.Fatalf("Expected the Parameters to not be nil!")
}
if deployment.Properties.Template == nil {
t.Fatalf("Expected the Template to not be nil!")
}
}
func TestFactoryShouldCreateDeploymentInstance(t *testing.T) {
testSubject := newDeploymentFactory(Linux)
deployment, err := testSubject.create(getTemplateParameters())
if err != nil {
t.Fatalf("ERROR: %s\n", err)
}
// spot check well known values to ensure correct serialization.
parametersMap := *deployment.Properties.Parameters
if _, ok := parametersMap["adminUsername"]; ok == false {
t.Fatalf("Expected the parameter value 'adminUsername' to be set, but it was not")
}
templateMap := *deployment.Properties.Template
if _, ok := templateMap["contentVersion"]; ok == false {
t.Fatalf("Expected the parameter value 'contentVersion' to be set, but it was not")
}
}
func TestMalformedTemplatesShouldReturnError(t *testing.T) {
testSubject := newDeploymentFactory("")
_, err := testSubject.create(getTemplateParameters())
if err == nil {
t.Fatalf("Expected an error, but did not receive one!\n")
}
}
func getTemplateParameters() TemplateParameters {
templateParameters := TemplateParameters{
AdminUsername: &TemplateParameter{"adminusername00"},
DnsNameForPublicIP: &TemplateParameter{"dnsnameforpublicip00"},
OSDiskName: &TemplateParameter{"osdiskname00"},
SshAuthorizedKey: &TemplateParameter{"sshkeydata00"},
StorageAccountBlobEndpoint: &TemplateParameter{"storageaccountblobendpoint00"},
VMName: &TemplateParameter{"vmname00"},
VMSize: &TemplateParameter{"vmsize00"},
}
return templateParameters
}

View File

@ -13,28 +13,29 @@ import (
) )
type StepDeployTemplate struct { type StepDeployTemplate struct {
client *AzureClient client *AzureClient
template string deploy func(resourceGroupName string, deploymentName string, cancelCh <-chan struct{}) error
deploy func(resourceGroupName string, deploymentName string, templateParameters *TemplateParameters, cancelCh <-chan struct{}) error say func(message string)
say func(message string) error func(e error)
error func(e error) config *Config
factory templateFactoryFunc
} }
func NewStepDeployTemplate(client *AzureClient, ui packer.Ui, template string) *StepDeployTemplate { func NewStepDeployTemplate(client *AzureClient, ui packer.Ui, config *Config, factory templateFactoryFunc) *StepDeployTemplate {
var step = &StepDeployTemplate{ var step = &StepDeployTemplate{
client: client, client: client,
template: template, say: func(message string) { ui.Say(message) },
say: func(message string) { ui.Say(message) }, error: func(e error) { ui.Error(e.Error()) },
error: func(e error) { ui.Error(e.Error()) }, config: config,
factory: factory,
} }
step.deploy = step.deployTemplate step.deploy = step.deployTemplate
return step return step
} }
func (s *StepDeployTemplate) deployTemplate(resourceGroupName string, deploymentName string, templateParameters *TemplateParameters, cancelCh <-chan struct{}) error { func (s *StepDeployTemplate) deployTemplate(resourceGroupName string, deploymentName string, cancelCh <-chan struct{}) error {
factory := newDeploymentFactory(s.template) deployment, err := s.factory(s.config)
deployment, err := factory.create(*templateParameters)
if err != nil { if err != nil {
return err return err
} }
@ -70,7 +71,6 @@ func (s *StepDeployTemplate) Run(state multistep.StateBag) multistep.StepAction
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
var deploymentName = state.Get(constants.ArmDeploymentName).(string) var deploymentName = state.Get(constants.ArmDeploymentName).(string)
var templateParameters = state.Get(constants.ArmTemplateParameters).(*TemplateParameters)
s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName))
s.say(fmt.Sprintf(" -> DeploymentName : '%s'", deploymentName)) s.say(fmt.Sprintf(" -> DeploymentName : '%s'", deploymentName))
@ -78,7 +78,7 @@ func (s *StepDeployTemplate) Run(state multistep.StateBag) multistep.StepAction
result := common.StartInterruptibleTask( result := common.StartInterruptibleTask(
func() bool { return common.IsStateCancelled(state) }, func() bool { return common.IsStateCancelled(state) },
func(cancelCh <-chan struct{}) error { func(cancelCh <-chan struct{}) error {
return s.deploy(resourceGroupName, deploymentName, templateParameters, cancelCh) return s.deploy(resourceGroupName, deploymentName, cancelCh)
}, },
) )

View File

@ -13,8 +13,7 @@ import (
func TestStepDeployTemplateShouldFailIfDeployFails(t *testing.T) { func TestStepDeployTemplateShouldFailIfDeployFails(t *testing.T) {
var testSubject = &StepDeployTemplate{ var testSubject = &StepDeployTemplate{
template: Linux, deploy: func(string, string, <-chan struct{}) error {
deploy: func(string, string, *TemplateParameters, <-chan struct{}) error {
return fmt.Errorf("!! Unit Test FAIL !!") return fmt.Errorf("!! Unit Test FAIL !!")
}, },
say: func(message string) {}, say: func(message string) {},
@ -35,10 +34,9 @@ func TestStepDeployTemplateShouldFailIfDeployFails(t *testing.T) {
func TestStepDeployTemplateShouldPassIfDeployPasses(t *testing.T) { func TestStepDeployTemplateShouldPassIfDeployPasses(t *testing.T) {
var testSubject = &StepDeployTemplate{ var testSubject = &StepDeployTemplate{
template: Linux, deploy: func(string, string, <-chan struct{}) error { return nil },
deploy: func(string, string, *TemplateParameters, <-chan struct{}) error { return nil }, say: func(message string) {},
say: func(message string) {}, error: func(e error) {},
error: func(e error) {},
} }
stateBag := createTestStateBagStepDeployTemplate() stateBag := createTestStateBagStepDeployTemplate()
@ -56,14 +54,11 @@ func TestStepDeployTemplateShouldPassIfDeployPasses(t *testing.T) {
func TestStepDeployTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) { func TestStepDeployTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) {
var actualResourceGroupName string var actualResourceGroupName string
var actualDeploymentName string var actualDeploymentName string
var actualTemplateParameters *TemplateParameters
var testSubject = &StepDeployTemplate{ var testSubject = &StepDeployTemplate{
template: Linux, deploy: func(resourceGroupName string, deploymentName string, cancelCh <-chan struct{}) error {
deploy: func(resourceGroupName string, deploymentName string, templateParameter *TemplateParameters, cancelCh <-chan struct{}) error {
actualResourceGroupName = resourceGroupName actualResourceGroupName = resourceGroupName
actualDeploymentName = deploymentName actualDeploymentName = deploymentName
actualTemplateParameters = templateParameter
return nil return nil
}, },
@ -80,7 +75,6 @@ func TestStepDeployTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) {
var expectedDeploymentName = stateBag.Get(constants.ArmDeploymentName).(string) var expectedDeploymentName = stateBag.Get(constants.ArmDeploymentName).(string)
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
var expectedTemplateParameters = stateBag.Get(constants.ArmTemplateParameters).(*TemplateParameters)
if actualDeploymentName != expectedDeploymentName { if actualDeploymentName != expectedDeploymentName {
t.Fatalf("Expected StepValidateTemplate to source 'constants.ArmDeploymentName' from the state bag, but it did not.") t.Fatalf("Expected StepValidateTemplate to source 'constants.ArmDeploymentName' from the state bag, but it did not.")
@ -89,10 +83,6 @@ func TestStepDeployTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) {
if actualResourceGroupName != expectedResourceGroupName { if actualResourceGroupName != expectedResourceGroupName {
t.Fatalf("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") t.Fatalf("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
} }
if actualTemplateParameters != expectedTemplateParameters {
t.Fatalf("Expected the step to source 'constants.ArmTemplateParameters' from the state bag, but it did not.")
}
} }
func createTestStateBagStepDeployTemplate() multistep.StateBag { func createTestStateBagStepDeployTemplate() multistep.StateBag {
@ -100,7 +90,6 @@ func createTestStateBagStepDeployTemplate() multistep.StateBag {
stateBag.Put(constants.ArmDeploymentName, "Unit Test: DeploymentName") stateBag.Put(constants.ArmDeploymentName, "Unit Test: DeploymentName")
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
stateBag.Put(constants.ArmTemplateParameters, &TemplateParameters{})
return stateBag return stateBag
} }

View File

@ -31,7 +31,6 @@ func (s *StepSetCertificate) Run(state multistep.StateBag) multistep.StepAction
var winRMCertificateUrl = state.Get(constants.ArmCertificateUrl).(string) var winRMCertificateUrl = state.Get(constants.ArmCertificateUrl).(string)
s.config.tmpWinRMCertificateUrl = winRMCertificateUrl s.config.tmpWinRMCertificateUrl = winRMCertificateUrl
state.Put(constants.ArmTemplateParameters, s.config.toTemplateParameters())
return multistep.ActionContinue return multistep.ActionContinue
} }

View File

@ -30,8 +30,9 @@ func TestStepSetCertificateShouldPassIfGetPasses(t *testing.T) {
} }
func TestStepSetCertificateShouldTakeStepArgumentsFromStateBag(t *testing.T) { func TestStepSetCertificateShouldTakeStepArgumentsFromStateBag(t *testing.T) {
config := new(Config)
var testSubject = &StepSetCertificate{ var testSubject = &StepSetCertificate{
config: new(Config), config: config,
say: func(message string) {}, say: func(message string) {},
error: func(e error) {}, error: func(e error) {},
} }
@ -43,9 +44,8 @@ func TestStepSetCertificateShouldTakeStepArgumentsFromStateBag(t *testing.T) {
t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result) t.Fatalf("Expected the step to return 'ActionContinue', but got '%d'.", result)
} }
_, ok := stateBag.GetOk(constants.ArmTemplateParameters) if config.tmpWinRMCertificateUrl != stateBag.Get(constants.ArmCertificateUrl) {
if !ok { t.Fatalf("Expected config.tmpWinRMCertificateUrl to be %s, but got %s'", stateBag.Get(constants.ArmCertificateUrl), config.tmpWinRMCertificateUrl)
t.Fatalf("Expected the state bag to have a value for '%s', but it did not.", constants.ArmTemplateParameters)
} }
} }

View File

@ -13,28 +13,28 @@ import (
type StepValidateTemplate struct { type StepValidateTemplate struct {
client *AzureClient client *AzureClient
template string validate func(resourceGroupName string, deploymentName string) error
validate func(resourceGroupName string, deploymentName string, templateParameters *TemplateParameters) error
say func(message string) say func(message string)
error func(e error) error func(e error)
config *Config
factory templateFactoryFunc
} }
func NewStepValidateTemplate(client *AzureClient, ui packer.Ui, template string) *StepValidateTemplate { func NewStepValidateTemplate(client *AzureClient, ui packer.Ui, config *Config, factory templateFactoryFunc) *StepValidateTemplate {
var step = &StepValidateTemplate{ var step = &StepValidateTemplate{
client: client, client: client,
template: template, say: func(message string) { ui.Say(message) },
say: func(message string) { ui.Say(message) }, error: func(e error) { ui.Error(e.Error()) },
error: func(e error) { ui.Error(e.Error()) }, config: config,
factory: factory,
} }
step.validate = step.validateTemplate step.validate = step.validateTemplate
return step return step
} }
func (s *StepValidateTemplate) validateTemplate(resourceGroupName string, deploymentName string, templateParameters *TemplateParameters) error { func (s *StepValidateTemplate) validateTemplate(resourceGroupName string, deploymentName string) error {
factory := newDeploymentFactory(s.template) deployment, err := s.factory(s.config)
deployment, err := factory.create(*templateParameters)
if err != nil { if err != nil {
return err return err
} }
@ -48,12 +48,11 @@ func (s *StepValidateTemplate) Run(state multistep.StateBag) multistep.StepActio
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string) var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
var deploymentName = state.Get(constants.ArmDeploymentName).(string) var deploymentName = state.Get(constants.ArmDeploymentName).(string)
var templateParameters = state.Get(constants.ArmTemplateParameters).(*TemplateParameters)
s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName)) s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName))
s.say(fmt.Sprintf(" -> DeploymentName : '%s'", deploymentName)) s.say(fmt.Sprintf(" -> DeploymentName : '%s'", deploymentName))
err := s.validate(resourceGroupName, deploymentName, templateParameters) err := s.validate(resourceGroupName, deploymentName)
return processStepResult(err, s.error, state) return processStepResult(err, s.error, state)
} }

View File

@ -12,10 +12,8 @@ import (
) )
func TestStepValidateTemplateShouldFailIfValidateFails(t *testing.T) { func TestStepValidateTemplateShouldFailIfValidateFails(t *testing.T) {
var testSubject = &StepValidateTemplate{ var testSubject = &StepValidateTemplate{
template: Linux, validate: func(string, string) error { return fmt.Errorf("!! Unit Test FAIL !!") },
validate: func(string, string, *TemplateParameters) error { return fmt.Errorf("!! Unit Test FAIL !!") },
say: func(message string) {}, say: func(message string) {},
error: func(e error) {}, error: func(e error) {},
} }
@ -34,8 +32,7 @@ func TestStepValidateTemplateShouldFailIfValidateFails(t *testing.T) {
func TestStepValidateTemplateShouldPassIfValidatePasses(t *testing.T) { func TestStepValidateTemplateShouldPassIfValidatePasses(t *testing.T) {
var testSubject = &StepValidateTemplate{ var testSubject = &StepValidateTemplate{
template: Linux, validate: func(string, string) error { return nil },
validate: func(string, string, *TemplateParameters) error { return nil },
say: func(message string) {}, say: func(message string) {},
error: func(e error) {}, error: func(e error) {},
} }
@ -55,14 +52,11 @@ func TestStepValidateTemplateShouldPassIfValidatePasses(t *testing.T) {
func TestStepValidateTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) { func TestStepValidateTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) {
var actualResourceGroupName string var actualResourceGroupName string
var actualDeploymentName string var actualDeploymentName string
var actualTemplateParameters *TemplateParameters
var testSubject = &StepValidateTemplate{ var testSubject = &StepValidateTemplate{
template: Linux, validate: func(resourceGroupName string, deploymentName string) error {
validate: func(resourceGroupName string, deploymentName string, templateParameter *TemplateParameters) error {
actualResourceGroupName = resourceGroupName actualResourceGroupName = resourceGroupName
actualDeploymentName = deploymentName actualDeploymentName = deploymentName
actualTemplateParameters = templateParameter
return nil return nil
}, },
@ -79,7 +73,6 @@ func TestStepValidateTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) {
var expectedDeploymentName = stateBag.Get(constants.ArmDeploymentName).(string) var expectedDeploymentName = stateBag.Get(constants.ArmDeploymentName).(string)
var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string) var expectedResourceGroupName = stateBag.Get(constants.ArmResourceGroupName).(string)
var expectedTemplateParameters = stateBag.Get(constants.ArmTemplateParameters).(*TemplateParameters)
if actualDeploymentName != expectedDeploymentName { if actualDeploymentName != expectedDeploymentName {
t.Fatalf("Expected the step to source 'constants.ArmDeploymentName' from the state bag, but it did not.") t.Fatalf("Expected the step to source 'constants.ArmDeploymentName' from the state bag, but it did not.")
@ -88,10 +81,6 @@ func TestStepValidateTemplateShouldTakeStepArgumentsFromStateBag(t *testing.T) {
if actualResourceGroupName != expectedResourceGroupName { if actualResourceGroupName != expectedResourceGroupName {
t.Fatalf("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.") t.Fatalf("Expected the step to source 'constants.ArmResourceGroupName' from the state bag, but it did not.")
} }
if actualTemplateParameters != expectedTemplateParameters {
t.Fatalf("Expected the step to source 'constants.ArmTemplateParameters' from the state bag, but it did not.")
}
} }
func createTestStateBagStepValidateTemplate() multistep.StateBag { func createTestStateBagStepValidateTemplate() multistep.StateBag {
@ -99,7 +88,6 @@ func createTestStateBagStepValidateTemplate() multistep.StateBag {
stateBag.Put(constants.ArmDeploymentName, "Unit Test: DeploymentName") stateBag.Put(constants.ArmDeploymentName, "Unit Test: DeploymentName")
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName") stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
stateBag.Put(constants.ArmTemplateParameters, &TemplateParameters{})
return stateBag return stateBag
} }

View File

@ -5,213 +5,18 @@ package arm
// See https://github.com/Azure/azure-quickstart-templates for a extensive list of templates. // See https://github.com/Azure/azure-quickstart-templates for a extensive list of templates.
const Linux = `{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"parameters": {
"adminUsername": {
"type": "string"
},
"adminPassword": {
"type": "string"
},
"dnsNameForPublicIP": {
"type": "string"
},
"imagePublisher": {
"type": "string"
},
"imageOffer": {
"type": "string"
},
"imageSku": {
"type": "string"
},
"imageVersion": {
"type": "string"
},
"osDiskName": {
"type": "string"
},
"sshAuthorizedKey": {
"type": "string"
},
"storageAccountBlobEndpoint": {
"type": "string"
},
"vmSize": {
"type": "string"
},
"vmName": {
"type": "string"
}
},
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2015-06-15",
"location": "[resourceGroup().location]",
"nicName": "packerNic",
"publicIPAddressName": "packerPublicIP",
"publicIPAddressType": "Dynamic",
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
"subnetName": "packerSubnet",
"subnetAddressPrefix": "10.0.0.0/24",
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"virtualNetworkName": "packerNetwork",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[variables('publicIPAddressName')]",
"location": "[variables('location')]",
"properties": {
"publicIPAllocationMethod": "[variables('publicIPAddressType')]",
"dnsSettings": {
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
}
}
},
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Network/virtualNetworks",
"name": "[variables('virtualNetworkName')]",
"location": "[variables('location')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"subnets": [
{
"name": "[variables('subnetName')]",
"properties": {
"addressPrefix": "[variables('subnetAddressPrefix')]"
}
}
]
}
},
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Network/networkInterfaces",
"name": "[variables('nicName')]",
"location": "[variables('location')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
},
"subnet": {
"id": "[variables('subnetRef')]"
}
}
}
]
}
},
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Compute/virtualMachines",
"name": "[parameters('vmName')]",
"location": "[variables('location')]",
"dependsOn": [
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
],
"properties": {
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"osProfile": {
"computerName": "[parameters('vmName')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]",
"linuxConfiguration": {
"disablePasswordAuthentication": "false",
"ssh": {
"publicKeys": [
{
"path": "[variables('sshKeyPath')]",
"keyData": "[parameters('sshAuthorizedKey')]"
}
]
}
}
},
"storageProfile": {
"imageReference": {
"publisher": "[parameters('imagePublisher')]",
"offer": "[parameters('imageOffer')]",
"sku": "[parameters('imageSku')]",
"version": "[parameters('imageVersion')]"
},
"osDisk": {
"name": "osdisk",
"vhd": {
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
},
"caching": "ReadWrite",
"createOption": "FromImage"
}
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
}
]
},
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": "false"
}
}
}
}
]
}`
// Template to deploy a KeyVault. // Template to deploy a KeyVault.
// //
// NOTE: the parameters for the KeyVault template are identical to Windows // This template is still hard-coded unlike the ARM templates used for VMs for
// template. Keeping these values in sync simplifies the code at the expense // a couple of reasons.
// of template bloat. This bloat may be addressed in the future. //
// 1. The SDK defines no types for a Key Vault
// 2. The Key Vault template is relatively simple, and is static.
//
const KeyVault = `{ const KeyVault = `{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
"contentVersion": "1.0.0.0", "contentVersion": "1.0.0.0",
"parameters": { "parameters": {
"adminUserName": {
"type": "string"
},
"adminPassword": {
"type": "securestring"
},
"dnsNameForPublicIP": {
"type": "string"
},
"imageOffer": {
"type": "string"
},
"imagePublisher": {
"type": "string"
},
"imageSku": {
"type": "string"
},
"imageVersion": {
"type": "string"
},
"keyVaultName": { "keyVaultName": {
"type": "string" "type": "string"
}, },
@ -221,23 +26,8 @@ const KeyVault = `{
"objectId": { "objectId": {
"type": "string" "type": "string"
}, },
"osDiskName": {
"type": "string"
},
"storageAccountBlobEndpoint": {
"type": "string"
},
"tenantId": { "tenantId": {
"type": "string" "type": "string"
},
"vmName": {
"type": "string"
},
"vmSize": {
"type": "string"
},
"winRMCertificateUrl": {
"type": "string"
} }
}, },
"variables": { "variables": {
@ -286,204 +76,3 @@ const KeyVault = `{
} }
] ]
}` }`
const Windows = `{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"parameters": {
"adminUserName": {
"type": "string"
},
"adminPassword": {
"type": "securestring"
},
"dnsNameForPublicIP": {
"type": "string"
},
"imageOffer": {
"type": "string"
},
"imagePublisher": {
"type": "string"
},
"imageSku": {
"type": "string"
},
"imageVersion": {
"type": "string"
},
"keyVaultName": {
"type": "string"
},
"keyVaultSecretValue": {
"type": "securestring"
},
"objectId": {
"type": "string"
},
"osDiskName": {
"type": "string"
},
"storageAccountBlobEndpoint": {
"type": "string"
},
"tenantId": {
"type": "string"
},
"vmName": {
"type": "string"
},
"vmSize": {
"type": "string"
},
"winRMCertificateUrl": {
"type": "string"
}
},
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2015-06-15",
"location": "[resourceGroup().location]",
"nicName": "packerNic",
"publicIPAddressName": "packerPublicIP",
"publicIPAddressType": "Dynamic",
"subnetName": "packerSubnet",
"subnetAddressPrefix": "10.0.0.0/24",
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"virtualNetworkName": "packerNetwork",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[variables('publicIPAddressName')]",
"location": "[variables('location')]",
"properties": {
"publicIPAllocationMethod": "[variables('publicIPAddressType')]",
"dnsSettings": {
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
}
}
},
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Network/virtualNetworks",
"name": "[variables('virtualNetworkName')]",
"location": "[variables('location')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"subnets": [
{
"name": "[variables('subnetName')]",
"properties": {
"addressPrefix": "[variables('subnetAddressPrefix')]"
}
}
]
}
},
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Network/networkInterfaces",
"name": "[variables('nicName')]",
"location": "[variables('location')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
},
"subnet": {
"id": "[variables('subnetRef')]"
}
}
}
]
}
},
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Compute/virtualMachines",
"name": "[parameters('vmName')]",
"location": "[variables('location')]",
"dependsOn": [
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
],
"properties": {
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"osProfile": {
"computerName": "[parameters('vmName')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]",
"secrets": [
{
"sourceVault": {
"id": "[resourceId(resourceGroup().name, 'Microsoft.KeyVault/vaults', parameters('keyVaultName'))]"
},
"vaultCertificates": [
{
"certificateUrl": "[parameters('winRMCertificateUrl')]",
"certificateStore": "My"
}
]
}
],
"windowsConfiguration": {
"provisionVMAgent": "true",
"winRM": {
"listeners": [{
"protocol": "https",
"certificateUrl": "[parameters('winRMCertificateUrl')]"
}
]
},
"enableAutomaticUpdates": "true"
}
},
"storageProfile": {
"imageReference": {
"publisher": "[parameters('imagePublisher')]",
"offer": "[parameters('imageOffer')]",
"sku": "[parameters('imageSku')]",
"version": "[parameters('imageVersion')]"
},
"osDisk": {
"name": "osdisk",
"vhd": {
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
},
"caching": "ReadWrite",
"createOption": "FromImage"
}
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
}
]
},
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": "false"
}
}
}
}
]
}`

View File

@ -0,0 +1,83 @@
package arm
import (
"encoding/json"
"github.com/Azure/azure-sdk-for-go/arm/compute"
"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
"github.com/mitchellh/packer/builder/azure/common/constants"
"github.com/mitchellh/packer/builder/azure/common/template"
)
type templateFactoryFunc func(*Config) (*resources.Deployment, error)
func GetKeyVaultDeployment(config *Config) (*resources.Deployment, error) {
params := &template.TemplateParameters{
KeyVaultName: &template.TemplateParameter{Value: config.tmpKeyVaultName},
KeyVaultSecretValue: &template.TemplateParameter{Value: config.winrmCertificate},
ObjectId: &template.TemplateParameter{Value: config.ObjectID},
TenantId: &template.TemplateParameter{Value: config.TenantID},
}
return createDeploymentParameters(KeyVault, params)
}
func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error) {
params := &template.TemplateParameters{
AdminUsername: &template.TemplateParameter{Value: config.UserName},
AdminPassword: &template.TemplateParameter{Value: config.Password},
DnsNameForPublicIP: &template.TemplateParameter{Value: config.tmpComputeName},
OSDiskName: &template.TemplateParameter{Value: config.tmpOSDiskName},
StorageAccountBlobEndpoint: &template.TemplateParameter{Value: config.storageAccountBlobEndpoint},
VMSize: &template.TemplateParameter{Value: config.VMSize},
VMName: &template.TemplateParameter{Value: config.tmpComputeName},
}
builder, _ := template.NewTemplateBuilder()
osType := compute.Linux
switch config.OSType {
case constants.Target_Linux:
builder.BuildLinux(config.sshAuthorizedKey)
case constants.Target_Windows:
osType = compute.Windows
builder.BuildWindows(config.tmpKeyVaultName, config.tmpWinRMCertificateUrl)
}
if config.ImageUrl != "" {
builder.SetImageUrl(config.ImageUrl, osType)
} else {
builder.SetMarketPlaceImage(config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion)
}
doc, _ := builder.ToJSON()
return createDeploymentParameters(*doc, params)
}
func createDeploymentParameters(doc string, parameters *template.TemplateParameters) (*resources.Deployment, error) {
var template map[string]interface{}
err := json.Unmarshal(([]byte)(doc), &template)
if err != nil {
return nil, err
}
bs, err := json.Marshal(*parameters)
if err != nil {
return nil, err
}
var templateParameters map[string]interface{}
err = json.Unmarshal(bs, &templateParameters)
if err != nil {
return nil, err
}
return &resources.Deployment{
Properties: &resources.DeploymentProperties{
Mode: resources.Incremental,
Template: &template,
Parameters: &templateParameters,
},
}, nil
}

View File

@ -0,0 +1,255 @@
package arm
import (
"encoding/json"
"strings"
"testing"
"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
"github.com/mitchellh/packer/builder/azure/common/approvals"
"github.com/mitchellh/packer/builder/azure/common/constants"
"github.com/mitchellh/packer/builder/azure/common/template"
)
// Ensure the link values are not set, and the concrete values are set.
func TestVirtualMachineDeployment00(t *testing.T) {
c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())
deployment, err := GetVirtualMachineDeployment(c)
if err != nil {
t.Fatal(err)
}
if deployment.Properties.Mode != resources.Incremental {
t.Errorf("Expected deployment.Properties.Mode to be %s, but got %s", resources.Incremental, deployment.Properties.Mode)
}
if deployment.Properties.ParametersLink != nil {
t.Errorf("Expected the ParametersLink to be nil!")
}
if deployment.Properties.TemplateLink != nil {
t.Errorf("Expected the TemplateLink to be nil!")
}
if deployment.Properties.Parameters == nil {
t.Errorf("Expected the Parameters to not be nil!")
}
if deployment.Properties.Template == nil {
t.Errorf("Expected the Template to not be nil!")
}
}
// Ensure the Virtual Machine template is a valid JSON document.
func TestVirtualMachineDeployment01(t *testing.T) {
c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())
deployment, err := GetVirtualMachineDeployment(c)
if err != nil {
t.Fatal(err)
}
_, err = json.Marshal(deployment.Properties.Template)
if err != nil {
t.Fatal(err)
}
}
// Ensure the Virtual Machine template parameters are correct.
func TestVirtualMachineDeployment02(t *testing.T) {
c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())
deployment, err := GetVirtualMachineDeployment(c)
if err != nil {
t.Fatal(err)
}
bs, err := json.Marshal(deployment.Properties.Parameters)
if err != nil {
t.Fatal(err)
}
var params template.TemplateParameters
err = json.Unmarshal(bs, &params)
if err != nil {
t.Fatal(err)
}
if params.AdminUsername.Value != c.UserName {
t.Errorf("Expected template parameter 'AdminUsername' to be %s, but got %s.", params.AdminUsername.Value, c.UserName)
}
if params.AdminPassword.Value != c.tmpAdminPassword {
t.Errorf("Expected template parameter 'AdminPassword' to be %s, but got %s.", params.AdminPassword.Value, c.tmpAdminPassword)
}
if params.DnsNameForPublicIP.Value != c.tmpComputeName {
t.Errorf("Expected template parameter 'DnsNameForPublicIP' to be %s, but got %s.", params.DnsNameForPublicIP.Value, c.tmpComputeName)
}
if params.OSDiskName.Value != c.tmpOSDiskName {
t.Errorf("Expected template parameter 'OSDiskName' to be %s, but got %s.", params.OSDiskName.Value, c.tmpOSDiskName)
}
if params.StorageAccountBlobEndpoint.Value != c.storageAccountBlobEndpoint {
t.Errorf("Expected template parameter 'StorageAccountBlobEndpoint' to be %s, but got %s.", params.StorageAccountBlobEndpoint.Value, c.storageAccountBlobEndpoint)
}
if params.VMSize.Value != c.VMSize {
t.Errorf("Expected template parameter 'VMSize' to be %s, but got %s.", params.VMSize.Value, c.VMSize)
}
if params.VMName.Value != c.tmpComputeName {
t.Errorf("Expected template parameter 'VMName' to be %s, but got %s.", params.VMName.Value, c.tmpComputeName)
}
}
// Ensure the VM template is correct when using a market place image.
func TestVirtualMachineDeployment03(t *testing.T) {
m := getArmBuilderConfiguration()
m["image_publisher"] = "ImagePublisher"
m["image_offer"] = "ImageOffer"
m["image_sku"] = "ImageSku"
m["image_version"] = "ImageVersion"
c, _, _ := newConfig(m, getPackerConfiguration())
deployment, err := GetVirtualMachineDeployment(c)
if err != nil {
t.Fatal(err)
}
bs, err := json.MarshalIndent(deployment.Properties.Template, "", " ")
if err != nil {
t.Fatal(err)
}
reader := strings.NewReader(string(bs))
err = approvals.Verify(t, reader)
if err != nil {
t.Fatal(err)
}
}
// Ensure the VM template is correct when using a custom image.
func TestVirtualMachineDeployment04(t *testing.T) {
config := map[string]string{
"capture_name_prefix": "ignore",
"capture_container_name": "ignore",
"location": "ignore",
"image_url": "https://localhost/custom.vhd",
"storage_account": "ignore",
"subscription_id": "ignore",
"os_type": constants.Target_Linux,
"communicator": "none",
}
c, _, err := newConfig(config, getPackerConfiguration())
if err != nil {
t.Fatal(err)
}
deployment, err := GetVirtualMachineDeployment(c)
if err != nil {
t.Fatal(err)
}
bs, err := json.MarshalIndent(deployment.Properties.Template, "", " ")
if err != nil {
t.Fatal(err)
}
reader := strings.NewReader(string(bs))
err = approvals.Verify(t, reader)
if err != nil {
t.Fatal(err)
}
}
// Ensure the link values are not set, and the concrete values are set.
func TestKeyVaultDeployment00(t *testing.T) {
c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())
deployment, err := GetKeyVaultDeployment(c)
if err != nil {
t.Fatal(err)
}
if deployment.Properties.Mode != resources.Incremental {
t.Errorf("Expected deployment.Properties.Mode to be %s, but got %s", resources.Incremental, deployment.Properties.Mode)
}
if deployment.Properties.ParametersLink != nil {
t.Errorf("Expected the ParametersLink to be nil!")
}
if deployment.Properties.TemplateLink != nil {
t.Errorf("Expected the TemplateLink to be nil!")
}
if deployment.Properties.Parameters == nil {
t.Errorf("Expected the Parameters to not be nil!")
}
if deployment.Properties.Template == nil {
t.Errorf("Expected the Template to not be nil!")
}
}
// Ensure the KeyVault template is a valid JSON document.
func TestKeyVaultDeployment01(t *testing.T) {
c, _, _ := newConfig(getArmBuilderConfiguration(), getPackerConfiguration())
deployment, err := GetKeyVaultDeployment(c)
if err != nil {
t.Fatal(err)
}
_, err = json.Marshal(deployment.Properties.Template)
if err != nil {
t.Fatal(err)
}
}
// Ensure the KeyVault template parameters are correct.
func TestKeyVaultDeployment02(t *testing.T) {
c, _, _ := newConfig(getArmBuilderConfigurationWithWindows(), getPackerConfiguration())
deployment, err := GetKeyVaultDeployment(c)
if err != nil {
t.Fatal(err)
}
bs, err := json.Marshal(deployment.Properties.Parameters)
if err != nil {
t.Fatal(err)
}
var params template.TemplateParameters
err = json.Unmarshal(bs, &params)
if err != nil {
t.Fatal(err)
}
if params.ObjectId.Value != c.ObjectID {
t.Errorf("Expected template parameter 'ObjectId' to be %s, but got %s.", params.ObjectId.Value, c.ObjectID)
}
if params.TenantId.Value != c.TenantID {
t.Errorf("Expected template parameter 'TenantId' to be %s, but got %s.", params.TenantId.Value, c.TenantID)
}
if params.KeyVaultName.Value != c.tmpKeyVaultName {
t.Errorf("Expected template parameter 'KeyVaultName' to be %s, but got %s.", params.KeyVaultName.Value, c.tmpKeyVaultName)
}
if params.KeyVaultSecretValue.Value != c.winrmCertificate {
t.Errorf("Expected template parameter 'KeyVaultSecretValue' to be %s, but got %s.", params.KeyVaultSecretValue.Value, c.winrmCertificate)
}
}
// Ensure the KeyVault template is correct.
func TestKeyVaultDeployment03(t *testing.T) {
c, _, _ := newConfig(getArmBuilderConfigurationWithWindows(), getPackerConfiguration())
deployment, err := GetKeyVaultDeployment(c)
if err != nil {
t.Fatal(err)
}
bs, err := json.MarshalIndent(deployment.Properties.Template, "", " ")
if err != nil {
t.Fatal(err)
}
reader := strings.NewReader(string(bs))
err = approvals.Verify(t, reader)
if err != nil {
t.Fatal(err)
}
}

View File

@ -0,0 +1,122 @@
package approvals
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"runtime"
"strings"
"testing"
)
type testState struct {
pc uintptr
fullName string
name string
fileName string
fileLine int
}
func Verify(t *testing.T, reader io.Reader) error {
state, err := findTestMethod()
if err != nil {
return err
}
return state.compare(state.getApprovalFile(), reader)
}
func (s *testState) compare(approvalFile string, reader io.Reader) error {
received, err := ioutil.ReadAll(reader)
if err != nil {
return err
}
// Ideally, this should only be written if
// 1. the approval file does not exist
// 2. the results differ
err = s.dumpReceivedTestResult(received)
if err != nil {
return err
}
fh, err := os.Open(approvalFile)
if err != nil {
return err
}
defer fh.Close()
approved, err := ioutil.ReadAll(fh)
if err != nil {
return err
}
// The two sides are identical, nothing more to do.
if bytes.Compare(received, approved) == 0 {
return nil
}
return fmt.Errorf("failed to approved %s", s.name)
}
func (s *testState) dumpReceivedTestResult(bs []byte) error {
fn := s.getReceivedFile()
err := ioutil.WriteFile(fn, bs, 0644)
return err
}
func (s *testState) getReceivedFile() string {
return fmt.Sprintf("%s.received.txt", s.name)
}
func (s *testState) getApprovalFile() string {
return fmt.Sprintf("%s.approved.txt", s.name)
}
func newTestState(pc uintptr, f *runtime.Func) (*testState, error) {
state := &testState{
pc: pc,
fullName: f.Name(),
}
state.fileName, state.fileLine = f.FileLine(pc)
splits := strings.Split(state.fullName, ".")
state.name = splits[len(splits)-1]
return state, nil
}
// Walk the call stack, and try to find the test method that was executed.
// The test method is identified by looking for the test runner, which is
// *assumed* to be common across all callers. The test runner has a Name() of
// 'testing.tRunner'. The method immediately previous to this is the test
// method.
func findTestMethod() (*testState, error) {
pc := make([]uintptr, 100)
count := runtime.Callers(0, pc)
i := 0
var lastFunc *runtime.Func
for ; i < count; i++ {
lastFunc = runtime.FuncForPC(pc[i])
if isTestRunner(lastFunc) {
break
}
}
if i == 0 || !isTestRunner(lastFunc) {
return nil, fmt.Errorf("approvals: could not find the test method")
}
testMethod := runtime.FuncForPC(pc[i-1])
return newTestState(pc[i-1], testMethod)
}
func isTestRunner(f *runtime.Func) bool {
return f != nil && f.Name() == "testing.tRunner"
}

View File

@ -1,10 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
package constants
const (
GOOS_Linux string = "linux"
GOOS_Windows string = "windows"
GOOS_Darwin string = "darwin"
)

View File

@ -5,31 +5,15 @@ package constants
// complete flags // complete flags
const ( const (
CertInstalled string = "certInstalled" AuthorizedKey string = "authorizedKey"
CertUploaded string = "certUploaded" Certificate string = "certificate"
DiskExists string = "diskExists" Error string = "error"
ImageCreated string = "imageCreated" PrivateKey string = "privateKey"
SrvExists string = "srvExists" SSHHost string = "sshHost"
VmExists string = "vmExists" Thumbprint string = "thumbprint"
VmRunning string = "vmRunning" Ui string = "ui"
) )
const ( const (
AuthorizedKey string = "authorizedKey"
Certificate string = "certificate"
Config string = "config"
Error string = "error"
HardDiskName string = "hardDiskName"
MediaLink string = "mediaLink"
OSImageName string = "osImageName"
PrivateKey string = "privateKey"
RequestManager string = "requestManager"
ServicePrincipalToken string = "servicePrincipalToken"
SSHHost string = "sshHost"
Thumbprint string = "thumbprint"
Ui string = "ui"
)
const (
ArmBlobEndpoint string = "arm.BlobEndpoint"
ArmCaptureTemplate string = "arm.CaptureTemplate" ArmCaptureTemplate string = "arm.CaptureTemplate"
ArmComputeName string = "arm.ComputeName" ArmComputeName string = "arm.ComputeName"
ArmCertificateUrl string = "arm.CertificateUrl" ArmCertificateUrl string = "arm.CertificateUrl"
@ -41,6 +25,5 @@ const (
ArmResourceGroupName string = "arm.ResourceGroupName" ArmResourceGroupName string = "arm.ResourceGroupName"
ArmIsResourceGroupCreated string = "arm.IsResourceGroupCreated" ArmIsResourceGroupCreated string = "arm.IsResourceGroupCreated"
ArmStorageAccountName string = "arm.StorageAccountName" ArmStorageAccountName string = "arm.StorageAccountName"
ArmTemplateParameters string = "arm.TemplateParameters"
ArmVirtualMachineCaptureParameters string = "arm.VirtualMachineCaptureParameters" ArmVirtualMachineCaptureParameters string = "arm.VirtualMachineCaptureParameters"
) )

View File

@ -59,7 +59,7 @@ func (s *StepCreateCert) createCert(state multistep.StateBag) error {
Bytes: x509.MarshalPKCS1PrivateKey(priv), Bytes: x509.MarshalPKCS1PrivateKey(priv),
})) }))
// Set the private key in the statebag for later // Set the private key in the state bag for later
state.Put(constants.PrivateKey, privkey) state.Put(constants.PrivateKey, privkey)
log.Printf("createCert: Private key:\n%s", privkey) log.Printf("createCert: Private key:\n%s", privkey)

View File

@ -0,0 +1,160 @@
{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"parameters": {
"adminPassword": {
"type": "string"
},
"adminUsername": {
"type": "string"
},
"dnsNameForPublicIP": {
"type": "string"
},
"osDiskName": {
"type": "string"
},
"storageAccountBlobEndpoint": {
"type": "string"
},
"vmName": {
"type": "string"
},
"vmSize": {
"type": "string"
}
},
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2015-06-15",
"location": "[resourceGroup().location]",
"nicName": "packerNic",
"publicIPAddressName": "packerPublicIP",
"publicIPAddressType": "Dynamic",
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
"subnetAddressPrefix": "10.0.0.0/24",
"subnetName": "packerSubnet",
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"virtualNetworkName": "packerNetwork",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"name": "[variables('publicIPAddressName')]",
"type": "Microsoft.Network/publicIPAddresses",
"location": "[variables('location')]",
"properties": {
"dnsSettings": {
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
},
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
}
},
{
"apiVersion": "[variables('apiVersion')]",
"name": "[variables('virtualNetworkName')]",
"type": "Microsoft.Network/virtualNetworks",
"location": "[variables('location')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"subnets": [
{
"properties": {
"addressPrefix": "[variables('subnetAddressPrefix')]"
},
"name": "[variables('subnetName')]"
}
]
}
},
{
"apiVersion": "[variables('apiVersion')]",
"name": "[variables('nicName')]",
"type": "Microsoft.Network/networkInterfaces",
"location": "[variables('location')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
],
"properties": {
"ipConfigurations": [
{
"properties": {
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[variables('subnetRef')]"
},
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
}
},
"name": "ipconfig"
}
]
}
},
{
"apiVersion": "[variables('apiVersion')]",
"name": "[parameters('vmName')]",
"type": "Microsoft.Compute/virtualMachines",
"location": "[variables('location')]",
"dependsOn": [
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
],
"properties": {
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": false
}
},
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
}
]
},
"osProfile": {
"computerName": "[parameters('vmName')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]",
"linuxConfiguration": {
"ssh": {
"publicKeys": [
{
"path": "[variables('sshKeyPath')]",
"keyData": "--test-ssh-authorized-key--"
}
]
}
}
},
"storageProfile": {
"imageReference": {
"publisher": "Canonical",
"offer": "UbuntuServer",
"sku": "16.04",
"version": "latest"
},
"osDisk": {
"name": "osdisk",
"vhd": {
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
},
"caching": "ReadWrite",
"createOption": "FromImage"
}
}
}
}
]
}

View File

@ -0,0 +1,158 @@
{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"parameters": {
"adminPassword": {
"type": "string"
},
"adminUsername": {
"type": "string"
},
"dnsNameForPublicIP": {
"type": "string"
},
"osDiskName": {
"type": "string"
},
"storageAccountBlobEndpoint": {
"type": "string"
},
"vmName": {
"type": "string"
},
"vmSize": {
"type": "string"
}
},
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2015-06-15",
"location": "[resourceGroup().location]",
"nicName": "packerNic",
"publicIPAddressName": "packerPublicIP",
"publicIPAddressType": "Dynamic",
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
"subnetAddressPrefix": "10.0.0.0/24",
"subnetName": "packerSubnet",
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"virtualNetworkName": "packerNetwork",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"name": "[variables('publicIPAddressName')]",
"type": "Microsoft.Network/publicIPAddresses",
"location": "[variables('location')]",
"properties": {
"dnsSettings": {
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
},
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
}
},
{
"apiVersion": "[variables('apiVersion')]",
"name": "[variables('virtualNetworkName')]",
"type": "Microsoft.Network/virtualNetworks",
"location": "[variables('location')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"subnets": [
{
"properties": {
"addressPrefix": "[variables('subnetAddressPrefix')]"
},
"name": "[variables('subnetName')]"
}
]
}
},
{
"apiVersion": "[variables('apiVersion')]",
"name": "[variables('nicName')]",
"type": "Microsoft.Network/networkInterfaces",
"location": "[variables('location')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
],
"properties": {
"ipConfigurations": [
{
"properties": {
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[variables('subnetRef')]"
},
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
}
},
"name": "ipconfig"
}
]
}
},
{
"apiVersion": "[variables('apiVersion')]",
"name": "[parameters('vmName')]",
"type": "Microsoft.Compute/virtualMachines",
"location": "[variables('location')]",
"dependsOn": [
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
],
"properties": {
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": false
}
},
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
}
]
},
"osProfile": {
"computerName": "[parameters('vmName')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]",
"linuxConfiguration": {
"ssh": {
"publicKeys": [
{
"path": "[variables('sshKeyPath')]",
"keyData": "--test-ssh-authorized-key--"
}
]
}
}
},
"storageProfile": {
"osDisk": {
"osType": "Linux",
"name": "osdisk",
"vhd": {
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
},
"image": {
"uri": "http://azure/custom.vhd"
},
"caching": "ReadWrite",
"createOption": "FromImage"
}
}
}
}
]
}

View File

@ -0,0 +1,174 @@
{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"parameters": {
"adminPassword": {
"type": "string"
},
"adminUsername": {
"type": "string"
},
"dnsNameForPublicIP": {
"type": "string"
},
"osDiskName": {
"type": "string"
},
"storageAccountBlobEndpoint": {
"type": "string"
},
"vmName": {
"type": "string"
},
"vmSize": {
"type": "string"
}
},
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2015-06-15",
"location": "[resourceGroup().location]",
"nicName": "packerNic",
"publicIPAddressName": "packerPublicIP",
"publicIPAddressType": "Dynamic",
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
"subnetAddressPrefix": "10.0.0.0/24",
"subnetName": "packerSubnet",
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"virtualNetworkName": "packerNetwork",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"name": "[variables('publicIPAddressName')]",
"type": "Microsoft.Network/publicIPAddresses",
"location": "[variables('location')]",
"properties": {
"dnsSettings": {
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
},
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
}
},
{
"apiVersion": "[variables('apiVersion')]",
"name": "[variables('virtualNetworkName')]",
"type": "Microsoft.Network/virtualNetworks",
"location": "[variables('location')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"subnets": [
{
"properties": {
"addressPrefix": "[variables('subnetAddressPrefix')]"
},
"name": "[variables('subnetName')]"
}
]
}
},
{
"apiVersion": "[variables('apiVersion')]",
"name": "[variables('nicName')]",
"type": "Microsoft.Network/networkInterfaces",
"location": "[variables('location')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
],
"properties": {
"ipConfigurations": [
{
"properties": {
"privateIPAllocationMethod": "Dynamic",
"subnet": {
"id": "[variables('subnetRef')]"
},
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
}
},
"name": "ipconfig"
}
]
}
},
{
"apiVersion": "[variables('apiVersion')]",
"name": "[parameters('vmName')]",
"type": "Microsoft.Compute/virtualMachines",
"location": "[variables('location')]",
"dependsOn": [
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
],
"properties": {
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": false
}
},
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
}
]
},
"osProfile": {
"computerName": "[parameters('vmName')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]",
"windowsConfiguration": {
"provisionVMAgent": true,
"winRM": {
"listeners": [
{
"protocol": "https",
"certificateUrl": "--test-winrm-certificate-url--"
}
]
}
},
"secrets": [
{
"sourceVault": {
"id": "[resourceId(resourceGroup().name, 'Microsoft.KeyVault/vaults', '--test-key-vault-name')]"
},
"vaultCertificates": [
{
"certificateUrl": "--test-winrm-certificate-url--",
"certificateStore": "My"
}
]
}
]
},
"storageProfile": {
"imageReference": {
"publisher": "MicrosoftWindowsServer",
"offer": "WindowsServer",
"sku": "2012-R2-Datacenter",
"version": "latest"
},
"osDisk": {
"name": "osdisk",
"vhd": {
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
},
"caching": "ReadWrite",
"createOption": "FromImage"
}
}
}
}
]
}

View File

@ -0,0 +1,50 @@
package template
import (
"github.com/Azure/azure-sdk-for-go/arm/compute"
"github.com/Azure/azure-sdk-for-go/arm/network"
//"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
)
/////////////////////////////////////////////////
// Template
type Template struct {
Schema *string `json:"$schema"`
ContentVersion *string `json:"contentVersion"`
Parameters *map[string]Parameters `json:"parameters"`
Variables *map[string]string `json:"variables"`
Resources *[]Resource `json:"resources"`
}
/////////////////////////////////////////////////
// Template > Parameters
type Parameters struct {
Type *string `json:"type"`
DefaultValue *string `json:"defaultValue,omitempty"`
}
/////////////////////////////////////////////////
// Template > Resource
type Resource struct {
ApiVersion *string `json:"apiVersion"`
Name *string `json:"name"`
Type *string `json:"type"`
Location *string `json:"location"`
DependsOn *[]string `json:"dependsOn,omitempty"`
Properties *Properties `json:"properties,omitempty"`
}
/////////////////////////////////////////////////
// Template > Resource > Properties
type Properties struct {
AddressSpace *network.AddressSpace `json:"addressSpace,omitempty"`
DiagnosticsProfile *compute.DiagnosticsProfile `json:"diagnosticsProfile,omitempty"`
DNSSettings *network.PublicIPAddressDNSSettings `json:"dnsSettings,omitempty"`
HardwareProfile *compute.HardwareProfile `json:"hardwareProfile,omitempty"`
IPConfigurations *[]network.IPConfiguration `json:"ipConfigurations,omitempty"`
NetworkProfile *compute.NetworkProfile `json:"networkProfile,omitempty"`
OsProfile *compute.OSProfile `json:"osProfile,omitempty"`
PublicIPAllocatedMethod *network.IPAllocationMethod `json:"publicIPAllocationMethod,omitempty"`
StorageProfile *compute.StorageProfile `json:"storageProfile,omitempty"`
Subnets *[]network.Subnet `json:"subnets,omitempty"`
}

View File

@ -0,0 +1,302 @@
package template
import (
"encoding/json"
"fmt"
"github.com/Azure/azure-sdk-for-go/arm/compute"
"github.com/Azure/go-autorest/autorest/to"
"strings"
)
const (
jsonPrefix = ""
jsonIndent = " "
resourceVirtualMachine = "Microsoft.Compute/virtualMachines"
resourceKeyVaults = "Microsoft.KeyVault/vaults"
variableSshKeyPath = "sshKeyPath"
)
type TemplateBuilder struct {
template *Template
}
func NewTemplateBuilder() (*TemplateBuilder, error) {
var t Template
err := json.Unmarshal([]byte(basicTemplate), &t)
if err != nil {
return nil, err
}
return &TemplateBuilder{
template: &t,
}, nil
}
func (s *TemplateBuilder) BuildLinux(sshAuthorizedKey string) error {
resource, err := s.getResourceByType(resourceVirtualMachine)
if err != nil {
return err
}
profile := resource.Properties.OsProfile
profile.LinuxConfiguration = &compute.LinuxConfiguration{
SSH: &compute.SSHConfiguration{
PublicKeys: &[]compute.SSHPublicKey{
compute.SSHPublicKey{
Path: to.StringPtr(s.toVariable(variableSshKeyPath)),
KeyData: to.StringPtr(sshAuthorizedKey),
},
},
},
}
return nil
}
func (s *TemplateBuilder) BuildWindows(keyVaultName, winRMCertificateUrl string) error {
resource, err := s.getResourceByType(resourceVirtualMachine)
if err != nil {
return err
}
profile := resource.Properties.OsProfile
profile.Secrets = &[]compute.VaultSecretGroup{
compute.VaultSecretGroup{
SourceVault: &compute.SubResource{
ID: to.StringPtr(s.toResourceID(resourceKeyVaults, keyVaultName)),
},
VaultCertificates: &[]compute.VaultCertificate{
compute.VaultCertificate{
CertificateStore: to.StringPtr("My"),
CertificateURL: to.StringPtr(winRMCertificateUrl),
},
},
},
}
profile.WindowsConfiguration = &compute.WindowsConfiguration{
ProvisionVMAgent: to.BoolPtr(true),
WinRM: &compute.WinRMConfiguration{
Listeners: &[]compute.WinRMListener{
compute.WinRMListener{
Protocol: "https",
CertificateURL: to.StringPtr(winRMCertificateUrl),
},
},
},
}
return nil
}
func (s *TemplateBuilder) SetMarketPlaceImage(publisher, offer, sku, version string) error {
resource, err := s.getResourceByType(resourceVirtualMachine)
if err != nil {
return err
}
profile := resource.Properties.StorageProfile
profile.ImageReference = &compute.ImageReference{
Publisher: to.StringPtr(publisher),
Offer: to.StringPtr(offer),
Sku: to.StringPtr(sku),
Version: to.StringPtr(version),
}
return nil
}
func (s *TemplateBuilder) SetImageUrl(imageUrl string, osType compute.OperatingSystemTypes) error {
resource, err := s.getResourceByType(resourceVirtualMachine)
if err != nil {
return err
}
profile := resource.Properties.StorageProfile
profile.OsDisk.OsType = osType
profile.OsDisk.Image = &compute.VirtualHardDisk{
URI: to.StringPtr(imageUrl),
}
return nil
}
func (s *TemplateBuilder) ToJSON() (*string, error) {
bs, err := json.MarshalIndent(s.template, jsonPrefix, jsonIndent)
if err != nil {
return nil, err
}
return to.StringPtr(string(bs)), err
}
func (s *TemplateBuilder) getResourceByType(t string) (*Resource, error) {
for _, x := range *s.template.Resources {
if strings.EqualFold(*x.Type, t) {
return &x, nil
}
}
return nil, fmt.Errorf("template: could not find a resource of type %s", t)
}
func (s *TemplateBuilder) toKeyVaultID(name string) string {
return s.toResourceID(resourceKeyVaults, name)
}
func (s *TemplateBuilder) toResourceID(id, name string) string {
return fmt.Sprintf("[resourceId(resourceGroup().name, '%s', '%s')]", id, name)
}
func (s *TemplateBuilder) toVariable(name string) string {
return fmt.Sprintf("[variables('%s')]", name)
}
const basicTemplate = `{
"$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json",
"contentVersion": "1.0.0.0",
"parameters": {
"adminUsername": {
"type": "string"
},
"adminPassword": {
"type": "string"
},
"dnsNameForPublicIP": {
"type": "string"
},
"osDiskName": {
"type": "string"
},
"storageAccountBlobEndpoint": {
"type": "string"
},
"vmSize": {
"type": "string"
},
"vmName": {
"type": "string"
}
},
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2015-06-15",
"location": "[resourceGroup().location]",
"nicName": "packerNic",
"publicIPAddressName": "packerPublicIP",
"publicIPAddressType": "Dynamic",
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
"subnetName": "packerSubnet",
"subnetAddressPrefix": "10.0.0.0/24",
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"virtualNetworkName": "packerNetwork",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[variables('publicIPAddressName')]",
"location": "[variables('location')]",
"properties": {
"publicIPAllocationMethod": "[variables('publicIPAddressType')]",
"dnsSettings": {
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
}
}
},
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Network/virtualNetworks",
"name": "[variables('virtualNetworkName')]",
"location": "[variables('location')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"subnets": [
{
"name": "[variables('subnetName')]",
"properties": {
"addressPrefix": "[variables('subnetAddressPrefix')]"
}
}
]
}
},
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Network/networkInterfaces",
"name": "[variables('nicName')]",
"location": "[variables('location')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
],
"properties": {
"ipConfigurations": [
{
"name": "ipconfig",
"properties": {
"privateIPAllocationMethod": "Dynamic",
"publicIPAddress": {
"id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
},
"subnet": {
"id": "[variables('subnetRef')]"
}
}
}
]
}
},
{
"apiVersion": "[variables('apiVersion')]",
"type": "Microsoft.Compute/virtualMachines",
"name": "[parameters('vmName')]",
"location": "[variables('location')]",
"dependsOn": [
"[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
],
"properties": {
"hardwareProfile": {
"vmSize": "[parameters('vmSize')]"
},
"osProfile": {
"computerName": "[parameters('vmName')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]"
},
"storageProfile": {
"osDisk": {
"name": "osdisk",
"vhd": {
"uri": "[concat(parameters('storageAccountBlobEndpoint'),variables('vmStorageAccountContainerName'),'/', parameters('osDiskName'),'.vhd')]"
},
"caching": "ReadWrite",
"createOption": "FromImage"
}
},
"networkProfile": {
"networkInterfaces": [
{
"id": "[resourceId('Microsoft.Network/networkInterfaces', variables('nicName'))]"
}
]
},
"diagnosticsProfile": {
"bootDiagnostics": {
"enabled": false
}
}
}
}
]
}`

View File

@ -0,0 +1,102 @@
package template
import (
"strings"
"testing"
"github.com/Azure/azure-sdk-for-go/arm/compute"
"github.com/mitchellh/packer/builder/azure/common/approvals"
)
// Ensure that a Linux template is configured as expected.
// * Include SSH configuration: authorized key, and key path.
func TestBuildLinux00(t *testing.T) {
testSubject, err := NewTemplateBuilder()
if err != nil {
t.Fatal(err)
}
err = testSubject.BuildLinux("--test-ssh-authorized-key--")
if err != nil {
t.Fatal(err)
}
err = testSubject.SetMarketPlaceImage("Canonical", "UbuntuServer", "16.04", "latest")
if err != nil {
t.Fatal(err)
}
doc, err := testSubject.ToJSON()
if err != nil {
t.Fatal(err)
}
reader := strings.NewReader(*doc)
err = approvals.Verify(t, reader)
if err != nil {
t.Fatal(err)
}
}
// Ensure that a user can specify a custom VHD when building a Linux template.
func TestBuildLinux01(t *testing.T) {
testSubject, err := NewTemplateBuilder()
if err != nil {
t.Fatal(err)
}
err = testSubject.BuildLinux("--test-ssh-authorized-key--")
if err != nil {
t.Fatal(err)
}
err = testSubject.SetImageUrl("http://azure/custom.vhd", compute.Linux)
if err != nil {
t.Fatal(err)
}
doc, err := testSubject.ToJSON()
if err != nil {
t.Fatal(err)
}
reader := strings.NewReader(*doc)
err = approvals.Verify(t, reader)
if err != nil {
t.Fatal(err)
}
}
// Ensure that a Windows template is configured as expected.
// * Include WinRM configuration.
// * Include KeyVault configuration, which is needed for WinRM.
func TestBuildWindows00(t *testing.T) {
testSubject, err := NewTemplateBuilder()
if err != nil {
t.Fatal(err)
}
err = testSubject.BuildWindows("--test-key-vault-name", "--test-winrm-certificate-url--")
if err != nil {
t.Fatal(err)
}
err = testSubject.SetMarketPlaceImage("MicrosoftWindowsServer", "WindowsServer", "2012-R2-Datacenter", "latest")
if err != nil {
t.Fatal(err)
}
doc, err := testSubject.ToJSON()
if err != nil {
t.Fatal(err)
}
reader := strings.NewReader(*doc)
err = approvals.Verify(t, reader)
if err != nil {
t.Fatal(err)
}
}

View File

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved. // Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information. // Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
package arm package template
// The intent of these types to facilitate interchange with Azure in the // The intent of these types to facilitate interchange with Azure in the
// appropriate JSON format. A sample format is below. Each parameter listed // appropriate JSON format. A sample format is below. Each parameter listed
@ -24,18 +24,12 @@ type TemplateParameters struct {
AdminUsername *TemplateParameter `json:"adminUsername,omitempty"` AdminUsername *TemplateParameter `json:"adminUsername,omitempty"`
AdminPassword *TemplateParameter `json:"adminPassword,omitempty"` AdminPassword *TemplateParameter `json:"adminPassword,omitempty"`
DnsNameForPublicIP *TemplateParameter `json:"dnsNameForPublicIP,omitempty"` DnsNameForPublicIP *TemplateParameter `json:"dnsNameForPublicIP,omitempty"`
ImageOffer *TemplateParameter `json:"imageOffer,omitempty"`
ImagePublisher *TemplateParameter `json:"imagePublisher,omitempty"`
ImageSku *TemplateParameter `json:"imageSku,omitempty"`
ImageVersion *TemplateParameter `json:"imageVersion,omitempty"`
KeyVaultName *TemplateParameter `json:"keyVaultName,omitempty"` KeyVaultName *TemplateParameter `json:"keyVaultName,omitempty"`
KeyVaultSecretValue *TemplateParameter `json:"keyVaultSecretValue,omitempty"` KeyVaultSecretValue *TemplateParameter `json:"keyVaultSecretValue,omitempty"`
ObjectId *TemplateParameter `json:"objectId,omitempty"` ObjectId *TemplateParameter `json:"objectId,omitempty"`
OSDiskName *TemplateParameter `json:"osDiskName,omitempty"` OSDiskName *TemplateParameter `json:"osDiskName,omitempty"`
SshAuthorizedKey *TemplateParameter `json:"sshAuthorizedKey,omitempty"`
StorageAccountBlobEndpoint *TemplateParameter `json:"storageAccountBlobEndpoint,omitempty"` StorageAccountBlobEndpoint *TemplateParameter `json:"storageAccountBlobEndpoint,omitempty"`
TenantId *TemplateParameter `json:"tenantId,omitempty"` TenantId *TemplateParameter `json:"tenantId,omitempty"`
VMSize *TemplateParameter `json:"vmSize,omitempty"` VMSize *TemplateParameter `json:"vmSize,omitempty"`
VMName *TemplateParameter `json:"vmName,omitempty"` VMName *TemplateParameter `json:"vmName,omitempty"`
WinRMCertificateUrl *TemplateParameter `json:"winRMCertificateUrl,omitempty"`
} }

View File

@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved. // Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See the LICENSE file in builder/azure for license information. // Licensed under the MIT License. See the LICENSE file in builder/azure for license information.
package arm package template
import ( import (
"encoding/json" "encoding/json"
@ -15,11 +15,7 @@ func TestTemplateParametersShouldHaveExpectedKeys(t *testing.T) {
AdminUsername: &TemplateParameter{"sentinel"}, AdminUsername: &TemplateParameter{"sentinel"},
AdminPassword: &TemplateParameter{"sentinel"}, AdminPassword: &TemplateParameter{"sentinel"},
DnsNameForPublicIP: &TemplateParameter{"sentinel"}, DnsNameForPublicIP: &TemplateParameter{"sentinel"},
ImageOffer: &TemplateParameter{"sentinel"},
ImagePublisher: &TemplateParameter{"sentinel"},
ImageSku: &TemplateParameter{"sentinel"},
OSDiskName: &TemplateParameter{"sentinel"}, OSDiskName: &TemplateParameter{"sentinel"},
SshAuthorizedKey: &TemplateParameter{"sentinel"},
StorageAccountBlobEndpoint: &TemplateParameter{"sentinel"}, StorageAccountBlobEndpoint: &TemplateParameter{"sentinel"},
VMName: &TemplateParameter{"sentinel"}, VMName: &TemplateParameter{"sentinel"},
VMSize: &TemplateParameter{"sentinel"}, VMSize: &TemplateParameter{"sentinel"},
@ -41,11 +37,7 @@ func TestTemplateParametersShouldHaveExpectedKeys(t *testing.T) {
"adminUsername", "adminUsername",
"adminPassword", "adminPassword",
"dnsNameForPublicIP", "dnsNameForPublicIP",
"imageOffer",
"imagePublisher",
"imageSku",
"osDiskName", "osDiskName",
"sshAuthorizedKey",
"storageAccountBlobEndpoint", "storageAccountBlobEndpoint",
"vmSize", "vmSize",
"vmName", "vmName",
@ -64,11 +56,7 @@ func TestParameterValuesShouldBeSet(t *testing.T) {
AdminUsername: &TemplateParameter{"adminusername00"}, AdminUsername: &TemplateParameter{"adminusername00"},
AdminPassword: &TemplateParameter{"adminpassword00"}, AdminPassword: &TemplateParameter{"adminpassword00"},
DnsNameForPublicIP: &TemplateParameter{"dnsnameforpublicip00"}, DnsNameForPublicIP: &TemplateParameter{"dnsnameforpublicip00"},
ImageOffer: &TemplateParameter{"imageoffer00"},
ImagePublisher: &TemplateParameter{"imagepublisher00"},
ImageSku: &TemplateParameter{"imagesku00"},
OSDiskName: &TemplateParameter{"osdiskname00"}, OSDiskName: &TemplateParameter{"osdiskname00"},
SshAuthorizedKey: &TemplateParameter{"sshauthorizedkey00"},
StorageAccountBlobEndpoint: &TemplateParameter{"storageaccountblobendpoint00"}, StorageAccountBlobEndpoint: &TemplateParameter{"storageaccountblobendpoint00"},
VMName: &TemplateParameter{"vmname00"}, VMName: &TemplateParameter{"vmname00"},
VMSize: &TemplateParameter{"vmsize00"}, VMSize: &TemplateParameter{"vmsize00"},

View File

@ -55,7 +55,6 @@ func (client *VaultClient) GetSecret(vaultName, secretName string) (*Secret, err
return nil, err return nil, err
} }
//resp, err := v.Send(req, http.StatusOK)
resp, err := autorest.SendWithSender(client, req) resp, err := autorest.SendWithSender(client, req)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -63,8 +63,14 @@ builder.
`USGovernment`. Defaults to `Public`. Long forms such as `USGovernment`. Defaults to `Public`. Long forms such as
`USGovernmentCloud` and `AzureUSGovernmentCloud` are also supported. `USGovernmentCloud` and `AzureUSGovernmentCloud` are also supported.
- `image_version` (string) Specify a specific version of an OS to boot from. - `image_version` (string) Specify a specific version of an OS to boot from. Defaults to `latest`. There may be a
Defaults to `latest`. difference in versions available across regions due to image synchronization latency. To ensure a consistent
version across regions set this value to one that is available in all regions where you are deploying.
CLI example `azure vm image list -l westus -p Canonical -o UbuntuServer -k 16.04.0-LTS`
- `image_url` (string) Specify a custom VHD to use. If this value is set, do not set image_publisher, image_offer,
image_sku, or image_version.
- `object_id` (string) Specify an OAuth Object ID to protect WinRM certificates - `object_id` (string) Specify an OAuth Object ID to protect WinRM certificates
created at runtime. This variable is required when creating images based on created at runtime. This variable is required when creating images based on