Support for building from custom managed images

This commit is contained in:
Christopher Boumenot 2017-05-28 21:06:09 -07:00
parent 8cea6f5be5
commit c6ff4aae59
24 changed files with 880 additions and 150 deletions

View File

@ -4,8 +4,8 @@
package arm
import (
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/autorest/azure"
)
type Authenticate struct {

View File

@ -18,10 +18,10 @@ import (
armStorage "github.com/Azure/azure-sdk-for-go/arm/storage"
"github.com/Azure/azure-sdk-for-go/storage"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/hashicorp/packer/builder/azure/common"
"github.com/hashicorp/packer/version"
"github.com/Azure/go-autorest/autorest/adal"
)
const (
@ -40,6 +40,7 @@ type AzureClient struct {
network.InterfacesClient
network.SubnetsClient
network.VirtualNetworksClient
compute.ImagesClient
compute.VirtualMachinesClient
common.VaultClient
armStorage.AccountsClient
@ -126,6 +127,12 @@ func NewAzureClient(subscriptionID, resourceGroupName, storageAccountName string
azureClient.GroupsClient.ResponseInspector = byInspecting(maxlen)
azureClient.GroupsClient.UserAgent += packerUserAgent
azureClient.ImagesClient = compute.NewImagesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
azureClient.ImagesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
azureClient.ImagesClient.RequestInspector = withInspection(maxlen)
azureClient.ImagesClient.ResponseInspector = byInspecting(maxlen)
azureClient.ImagesClient.UserAgent += packerUserAgent
azureClient.InterfacesClient = network.NewInterfacesClientWithBaseURI(cloud.ResourceManagerEndpoint, subscriptionID)
azureClient.InterfacesClient.Authorizer = autorest.NewBearerAuthorizer(servicePrincipalToken)
azureClient.InterfacesClient.RequestInspector = withInspection(maxlen)

View File

@ -15,11 +15,12 @@ import (
"github.com/hashicorp/packer/builder/azure/common/constants"
"github.com/hashicorp/packer/builder/azure/common/lin"
"github.com/Azure/azure-sdk-for-go/arm/storage"
"github.com/Azure/go-autorest/autorest/adal"
packerCommon "github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/helper/communicator"
"github.com/hashicorp/packer/packer"
"github.com/mitchellh/multistep"
"github.com/Azure/go-autorest/autorest/adal"
)
type Builder struct {
@ -47,6 +48,7 @@ func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
b.stateBag = new(multistep.BasicStateBag)
b.configureStateBag(b.stateBag)
b.setTemplateParameters(b.stateBag)
b.setImageParameters(b.stateBag)
return warnings, errs
}
@ -87,17 +89,39 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
return nil, err
}
b.config.storageAccountBlobEndpoint, err = b.getBlobEndpoint(azureClient, b.config.ResourceGroupName, b.config.StorageAccount)
if b.config.isManagedImage() {
group, err := azureClient.GroupsClient.Get(b.config.TargetManagedImageResourceGroupName)
if err != nil {
return nil, fmt.Errorf("Cannot locate the managed image resource group %s.", b.config.TargetManagedImageResourceGroupName)
}
b.config.targetManageImageLocation = *group.Location
// If a managed image already exists it cannot be overwritten.
_, err = azureClient.ImagesClient.Get(b.config.TargetManagedImageResourceGroupName, b.config.TargetManagedImageName, "")
if err == nil {
return nil, fmt.Errorf("A managed image named %s already exists in the resource group %s.", b.config.TargetManagedImageName, b.config.TargetManagedImageResourceGroupName)
}
}
account, err := b.getBlobAccount(azureClient, b.config.ResourceGroupName, b.config.StorageAccount)
if err != nil {
return nil, err
}
b.config.storageAccountBlobEndpoint = *account.AccountProperties.PrimaryEndpoints.Blob
if !b.config.isManagedImage() && equalLocation(*account.Location, b.config.Location) == false {
return nil, fmt.Errorf("The storage account is located in %s, but the build will take place in %s. The locations must be identical", *account.Location, b.config.Location)
}
endpointConnectType := PublicEndpoint
if b.isPrivateNetworkCommunication() {
endpointConnectType = PrivateEndpoint
}
b.setRuntimeParameters(b.stateBag)
b.setTemplateParameters(b.stateBag)
b.setImageParameters(b.stateBag)
var steps []multistep.Step
if b.config.OSType == constants.Target_Linux {
@ -198,13 +222,21 @@ func (b *Builder) Cancel() {
}
}
func (b *Builder) getBlobEndpoint(client *AzureClient, resourceGroupName string, storageAccountName string) (string, error) {
func equalLocation(location1, location2 string) bool {
return strings.EqualFold(canonicalizeLocation(location1), canonicalizeLocation(location2))
}
func canonicalizeLocation(location string) string {
return strings.Replace(location, " ", "", -1)
}
func (b *Builder) getBlobAccount(client *AzureClient, resourceGroupName string, storageAccountName string) (*storage.Account, error) {
account, err := client.AccountsClient.GetProperties(resourceGroupName, storageAccountName)
if err != nil {
return "", err
return nil, err
}
return *account.AccountProperties.PrimaryEndpoints.Blob, nil
return &account, err
}
func (b *Builder) configureStateBag(stateBag multistep.StateBag) {
@ -220,12 +252,25 @@ func (b *Builder) configureStateBag(stateBag multistep.StateBag) {
stateBag.Put(constants.ArmPublicIPAddressName, DefaultPublicIPAddressName)
stateBag.Put(constants.ArmResourceGroupName, b.config.tmpResourceGroupName)
stateBag.Put(constants.ArmStorageAccountName, b.config.StorageAccount)
stateBag.Put(constants.ArmIsManagedImage, b.config.isManagedImage())
stateBag.Put(constants.ArmTargetManagedImageResourceGroupName, b.config.TargetManagedImageResourceGroupName)
stateBag.Put(constants.ArmTargetManagedImageName, b.config.TargetManagedImageName)
}
// Parameters that are only known at runtime after querying Azure.
func (b *Builder) setRuntimeParameters(stateBag multistep.StateBag) {
stateBag.Put(constants.ArmTargetManagedImageLocation, b.config.targetManageImageLocation)
}
func (b *Builder) setTemplateParameters(stateBag multistep.StateBag) {
stateBag.Put(constants.ArmVirtualMachineCaptureParameters, b.config.toVirtualMachineCaptureParameters())
}
func (b *Builder) setImageParameters(stateBag multistep.StateBag) {
stateBag.Put(constants.ArmImageParameters, b.config.toImageParameters())
}
func (b *Builder) getServicePrincipalTokens(say func(string)) (*adal.ServicePrincipalToken, *adal.ServicePrincipalToken, error) {
var servicePrincipalToken *adal.ServicePrincipalToken
var servicePrincipalTokenVault *adal.ServicePrincipalToken

View File

@ -65,8 +65,19 @@ type Config struct {
ImageSku string `mapstructure:"image_sku"`
ImageVersion string `mapstructure:"image_version"`
ImageUrl string `mapstructure:"image_url"`
Location string `mapstructure:"location"`
VMSize string `mapstructure:"vm_size"`
ManagedImageResourceGroupName string `mapstructure:"managed_image_resource_group_name"`
ManagedImageName string `mapstructure:"managed_image_name"`
managedImageLocation string
managedImageBlobUri string
managedImageOSState compute.OperatingSystemStateTypes
Location string `mapstructure:"location"`
VMSize string `mapstructure:"vm_size"`
TargetManagedImageResourceGroupName string `mapstructure:"target_managed_image_resource_group_name"`
TargetManagedImageName string `mapstructure:"target_managed_image_name"`
targetManageImageLocation string
// Deployment
AzureTags map[string]*string `mapstructure:"azure_tags"`
@ -118,6 +129,14 @@ type keyVaultCertificate struct {
Password string `json:"password,omitempty"`
}
func (c *Config) toVMID() string {
return fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", c.SubscriptionID, c.tmpResourceGroupName, c.tmpComputeName)
}
func (c *Config) isManagedImage() bool {
return c.TargetManagedImageName != ""
}
func (c *Config) toVirtualMachineCaptureParameters() *compute.VirtualMachineCaptureParameters {
return &compute.VirtualMachineCaptureParameters{
DestinationContainerName: &c.CaptureContainerName,
@ -126,6 +145,18 @@ func (c *Config) toVirtualMachineCaptureParameters() *compute.VirtualMachineCapt
}
}
func (c *Config) toImageParameters() *compute.Image {
return &compute.Image{
ImageProperties: &compute.ImageProperties{
SourceVirtualMachine: &compute.SubResource{
ID: to.StringPtr(c.toVMID()),
},
},
Location: to.StringPtr(c.Location),
Tags: &c.AzureTags,
}
}
func (c *Config) createCertificate() (string, error) {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
@ -277,7 +308,9 @@ func setSshValues(c *Config) error {
func setWinRMCertificate(c *Config) error {
c.Comm.WinRMTransportDecorator =
func() winrm.Transporter { return &winrm.ClientNTLM{} }
func() winrm.Transporter {
return &winrm.ClientNTLM{}
}
cert, err := c.createCertificate()
c.winrmCertificate = cert
@ -372,7 +405,7 @@ func provideDefaultValues(c *Config) {
c.VMSize = DefaultVMSize
}
if c.ImageUrl == "" && c.ImageVersion == "" {
if c.ImagePublisher != "" && c.ImageVersion == "" {
c.ImageVersion = DefaultImageVersion
}
@ -467,7 +500,13 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
/////////////////////////////////////////////
// Compute
if c.ImageUrl == "" {
if c.ImageUrl != "" &&
(c.ManagedImageName != "" || c.ManagedImageResourceGroupName != "") &&
(c.ImagePublisher != "" || c.ImageOffer != "" || c.ImageSku != "") {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("Specify either a VHD (image_url), Image Reference (image_publisher, image_offer, image_sku) or a Managed Disk (managed_disk_image_name, managed_disk_resource_group_name"))
}
if c.ImageUrl == "" && c.ManagedImageName == "" {
if c.ImagePublisher == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("An image_publisher must be specified"))
}
@ -479,6 +518,19 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) {
if c.ImageSku == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("An image_sku must be specified"))
}
} else if c.ImageUrl == "" && c.ImagePublisher == "" {
if c.ManagedImageName == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("A managed_image_name must be specified"))
}
if c.ManagedImageResourceGroupName == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("An managed_image_resource_group_name must be specified"))
}
if c.TargetManagedImageName == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("An target_managed_image_name must be specified"))
}
if c.TargetManagedImageResourceGroupName == "" {
errs = packer.MultiErrorAppend(errs, fmt.Errorf("An target_managed_image_resource_group_name 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"))

View File

@ -10,6 +10,7 @@ package arm
import (
"fmt"
"github.com/Azure/azure-sdk-for-go/arm/compute"
"strings"
)
@ -43,6 +44,17 @@ func (s *resourceResolver) Resolve(c *Config) error {
c.VirtualNetworkSubnetName = subnetName
}
if s.shouldResolveManagedImageName(c) {
image, err := findManagedImageByName(s.client, c.ManagedImageName, c.ManagedImageResourceGroupName)
if err != nil {
return err
}
c.managedImageBlobUri = *image.ImageProperties.StorageProfile.OsDisk.BlobURI
c.managedImageLocation = *image.Location
c.managedImageOSState = image.ImageProperties.StorageProfile.OsDisk.OsState
}
return nil
}
@ -50,12 +62,31 @@ func (s *resourceResolver) shouldResolveResourceGroup(c *Config) bool {
return c.VirtualNetworkName != "" && c.VirtualNetworkResourceGroupName == ""
}
func (s *resourceResolver) shouldResolveManagedImageName(c *Config) bool {
return c.ManagedImageName != ""
}
func getResourceGroupNameFromId(id string) string {
// "/subscriptions/3f499422-dd76-4114-8859-86d526c9deb6/resourceGroups/packer-Resource-Group-yylnwsl30j/providers/...
xs := strings.Split(id, "/")
return xs[4]
}
func findManagedImageByName(client *AzureClient, name, resourceGroupName string) (*compute.Image, error) {
images, err := client.ImagesClient.ListByResourceGroup(resourceGroupName)
if err != nil {
return nil, err
}
for _, image := range *images.Value {
if strings.EqualFold(name, *image.Name) {
return &image, nil
}
}
return nil, fmt.Errorf("Cannot find an image named '%s' in the resource group '%s'", name, resourceGroupName)
}
func findVirtualNetworkResourceGroup(client *AzureClient, name string) (string, error) {
virtualNetworks, err := client.VirtualNetworksClient.ListAll()
if err != nil {

View File

@ -14,39 +14,49 @@ import (
)
type StepCaptureImage struct {
client *AzureClient
capture func(resourceGroupName string, computeName string, parameters *compute.VirtualMachineCaptureParameters, cancelCh <-chan struct{}) error
get func(client *AzureClient) *CaptureTemplate
say func(message string)
error func(e error)
client *AzureClient
generalizeVM func(resourceGroupName, computeName string) error
captureVhd func(resourceGroupName string, computeName string, parameters *compute.VirtualMachineCaptureParameters, cancelCh <-chan struct{}) error
captureManagedImage func(resourceGroupName string, computeName string, parameters *compute.Image, cancelCh <-chan struct{}) error
get func(client *AzureClient) *CaptureTemplate
say func(message string)
error func(e error)
}
func NewStepCaptureImage(client *AzureClient, ui packer.Ui) *StepCaptureImage {
var step = &StepCaptureImage{
client: client,
get: func(client *AzureClient) *CaptureTemplate { return client.Template },
say: func(message string) { ui.Say(message) },
error: func(e error) { ui.Error(e.Error()) },
get: func(client *AzureClient) *CaptureTemplate {
return client.Template
},
say: func(message string) {
ui.Say(message)
},
error: func(e error) {
ui.Error(e.Error())
},
}
step.capture = step.captureImage
step.generalizeVM = step.generalize
step.captureVhd = step.captureImage
step.captureManagedImage = step.captureImageFromVM
return step
}
func (s *StepCaptureImage) captureImage(resourceGroupName string, computeName string, parameters *compute.VirtualMachineCaptureParameters, cancelCh <-chan struct{}) error {
func (s *StepCaptureImage) generalize(resourceGroupName string, computeName string) error {
_, err := s.client.Generalize(resourceGroupName, computeName)
if err != nil {
return err
}
return err
}
func (s *StepCaptureImage) captureImageFromVM(resourceGroupName string, imageName string, image *compute.Image, cancelCh <-chan struct{}) error {
_, errChan := s.client.ImagesClient.CreateOrUpdate(resourceGroupName, imageName, *image, cancelCh)
return <-errChan
}
func (s *StepCaptureImage) captureImage(resourceGroupName string, computeName string, parameters *compute.VirtualMachineCaptureParameters, cancelCh <-chan struct{}) error {
_, errChan := s.client.Capture(resourceGroupName, computeName, *parameters, cancelCh)
err = <-errChan
if err != nil {
return err
}
return nil
return <-errChan
}
func (s *StepCaptureImage) Run(state multistep.StateBag) multistep.StepAction {
@ -54,15 +64,35 @@ func (s *StepCaptureImage) Run(state multistep.StateBag) multistep.StepAction {
var computeName = state.Get(constants.ArmComputeName).(string)
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
var parameters = state.Get(constants.ArmVirtualMachineCaptureParameters).(*compute.VirtualMachineCaptureParameters)
var vmCaptureParameters = state.Get(constants.ArmVirtualMachineCaptureParameters).(*compute.VirtualMachineCaptureParameters)
var imageParameters = state.Get(constants.ArmImageParameters).(*compute.Image)
s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName))
s.say(fmt.Sprintf(" -> ComputeName : '%s'", computeName))
var isManagedImage = state.Get(constants.ArmIsManagedImage).(bool)
var targetManagedImageResourceGroupName = state.Get(constants.ArmTargetManagedImageResourceGroupName).(string)
var targetManagedImageName = state.Get(constants.ArmTargetManagedImageName).(string)
var targetManagedImageLocation = state.Get(constants.ArmTargetManagedImageLocation).(string)
s.say(fmt.Sprintf(" -> ResourceGroupName : '%s'", resourceGroupName))
s.say(fmt.Sprintf(" -> ComputeName : '%s'", computeName))
result := common.StartInterruptibleTask(
func() bool { return common.IsStateCancelled(state) },
func() bool {
return common.IsStateCancelled(state)
},
func(cancelCh <-chan struct{}) error {
return s.capture(resourceGroupName, computeName, parameters, cancelCh)
err := s.generalizeVM(resourceGroupName, computeName)
if err != nil {
return err
}
if isManagedImage {
s.say(fmt.Sprintf(" -> ImageResourceGroupName : '%s'", targetManagedImageResourceGroupName))
s.say(fmt.Sprintf(" -> ImageName : '%s'", targetManagedImageName))
s.say(fmt.Sprintf(" -> Image Location : '%s'", targetManagedImageLocation))
return s.captureManagedImage(targetManagedImageResourceGroupName, targetManagedImageName, imageParameters, cancelCh)
} else {
return s.captureVhd(resourceGroupName, computeName, vmCaptureParameters, cancelCh)
}
})
// HACK(chrboum): I do not like this. The capture method should be returning this value

View File

@ -14,9 +14,12 @@ import (
func TestStepCaptureImageShouldFailIfCaptureFails(t *testing.T) {
var testSubject = &StepCaptureImage{
capture: func(string, string, *compute.VirtualMachineCaptureParameters, <-chan struct{}) error {
captureVhd: func(string, string, *compute.VirtualMachineCaptureParameters, <-chan struct{}) error {
return fmt.Errorf("!! Unit Test FAIL !!")
},
generalizeVM: func(string, string) error {
return nil
},
get: func(client *AzureClient) *CaptureTemplate {
return nil
},
@ -38,7 +41,10 @@ func TestStepCaptureImageShouldFailIfCaptureFails(t *testing.T) {
func TestStepCaptureImageShouldPassIfCapturePasses(t *testing.T) {
var testSubject = &StepCaptureImage{
capture: func(string, string, *compute.VirtualMachineCaptureParameters, <-chan struct{}) error { return nil },
captureVhd: func(string, string, *compute.VirtualMachineCaptureParameters, <-chan struct{}) error { return nil },
generalizeVM: func(string, string) error {
return nil
},
get: func(client *AzureClient) *CaptureTemplate {
return nil
},
@ -70,13 +76,16 @@ func TestStepCaptureImageShouldTakeStepArgumentsFromStateBag(t *testing.T) {
}
var testSubject = &StepCaptureImage{
capture: func(resourceGroupName string, computeName string, parameters *compute.VirtualMachineCaptureParameters, cancelCh <-chan struct{}) error {
captureVhd: func(resourceGroupName string, computeName string, parameters *compute.VirtualMachineCaptureParameters, cancelCh <-chan struct{}) error {
actualResourceGroupName = resourceGroupName
actualComputeName = computeName
actualVirtualMachineCaptureParameters = parameters
return nil
},
generalizeVM: func(string, string) error {
return nil
},
get: func(client *AzureClient) *CaptureTemplate {
return actualCaptureTemplate
},
@ -120,5 +129,11 @@ func createTestStateBagStepCaptureImage() multistep.StateBag {
stateBag.Put(constants.ArmResourceGroupName, "Unit Test: ResourceGroupName")
stateBag.Put(constants.ArmVirtualMachineCaptureParameters, &compute.VirtualMachineCaptureParameters{})
stateBag.Put(constants.ArmIsManagedImage, false)
stateBag.Put(constants.ArmTargetManagedImageResourceGroupName, "")
stateBag.Put(constants.ArmTargetManagedImageName, "")
stateBag.Put(constants.ArmTargetManagedImageLocation, "")
stateBag.Put(constants.ArmImageParameters, &compute.Image{})
return stateBag
}

View File

@ -71,7 +71,9 @@ func (s *StepCreateResourceGroup) Cleanup(state multistep.StateBag) {
ui.Say("\nCleanup requested, deleting resource group ...")
var resourceGroupName = state.Get(constants.ArmResourceGroupName).(string)
_, err := s.client.GroupsClient.Delete(resourceGroupName, nil)
_, errChan := s.client.GroupsClient.Delete(resourceGroupName, nil)
err := <-errChan
if err != nil {
ui.Error(fmt.Sprintf("Error deleting resource group. Please delete it manually.\n\n"+
"Name: %s\n"+

View File

@ -41,6 +41,13 @@ func (s *StepDeleteOSDisk) Run(state multistep.StateBag) multistep.StepAction {
s.say("Deleting the temporary OS disk ...")
var osDisk = state.Get(constants.ArmOSDiskVhd).(string)
var isManagedDisk = state.Get(constants.ArmIsManagedImage).(bool)
if isManagedDisk {
s.say(fmt.Sprintf(" -> OS Disk : skipping, managed disk was used..."))
return multistep.ActionContinue
}
s.say(fmt.Sprintf(" -> OS Disk : '%s'", osDisk))
u, err := url.Parse(osDisk)

View File

@ -108,6 +108,7 @@ func TestStepDeleteOSDiskShouldHandleComplexStorageContainerNames(t *testing.T)
func DeleteTestStateBagStepDeleteOSDisk(osDiskVhd string) multistep.StateBag {
stateBag := new(multistep.BasicStateBag)
stateBag.Put(constants.ArmOSDiskVhd, osDiskVhd)
stateBag.Put(constants.ArmIsManagedImage, false)
return stateBag
}

View File

@ -53,9 +53,16 @@ func (s *StepGetOSDisk) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionHalt
}
s.say(fmt.Sprintf(" -> OS Disk : '%s'", *vm.StorageProfile.OsDisk.Vhd.URI))
state.Put(constants.ArmOSDiskVhd, *vm.StorageProfile.OsDisk.Vhd.URI)
var vhdUri string
if vm.StorageProfile.OsDisk.Vhd != nil {
s.say(fmt.Sprintf(" -> OS Disk : '%s'", vhdUri))
vhdUri = *vm.StorageProfile.OsDisk.Vhd.URI
} else {
s.say(fmt.Sprintf(" -> Managed OS Disk : '%s'", vhdUri))
vhdUri = *vm.StorageProfile.OsDisk.ManagedDisk.ID
}
state.Put(constants.ArmOSDiskVhd, vhdUri)
return multistep.ActionContinue
}

View File

@ -51,6 +51,10 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error)
if config.ImageUrl != "" {
builder.SetImageUrl(config.ImageUrl, osType)
} else if config.ManagedImageName != "" {
builder.SetManagedDiskUrl(config.ManagedImageName, config.managedImageLocation, config.managedImageBlobUri, config.managedImageOSState)
} else if config.TargetManagedImageName != "" && config.ImagePublisher != "" {
builder.SetManagedMarketplaceImage(config.Location, config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion)
} else {
builder.SetMarketPlaceImage(config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion)
}

View File

@ -26,7 +26,7 @@
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"apiVersion": "[variables('publicIPAddressApiVersion')]",
"location": "[variables('location')]",
"name": "[variables('publicIPAddressName')]",
"properties": {
@ -38,7 +38,7 @@
"type": "Microsoft.Network/publicIPAddresses"
},
{
"apiVersion": "[variables('apiVersion')]",
"apiVersion": "[variables('virtualNetworksApiVersion')]",
"location": "[variables('location')]",
"name": "[variables('virtualNetworkName')]",
"properties": {
@ -59,7 +59,7 @@
"type": "Microsoft.Network/virtualNetworks"
},
{
"apiVersion": "[variables('apiVersion')]",
"apiVersion": "[variables('networkInterfacesApiVersion')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
@ -144,9 +144,12 @@
],
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2015-06-15",
"apiVersion": "2017-03-30",
"location": "[resourceGroup().location]",
"managedDiskApiVersion": "2017-03-30",
"networkInterfacesApiVersion": "2017-04-01",
"nicName": "packerNic",
"publicIPAddressApiVersion": "2017-04-01",
"publicIPAddressName": "packerPublicIP",
"publicIPAddressType": "Dynamic",
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
@ -155,6 +158,7 @@
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"virtualNetworkName": "packerNetwork",
"virtualNetworkResourceGroup": "[resourceGroup().name]",
"virtualNetworksApiVersion": "2017-04-01",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
}

View File

@ -26,7 +26,7 @@
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"apiVersion": "[variables('publicIPAddressApiVersion')]",
"location": "[variables('location')]",
"name": "[variables('publicIPAddressName')]",
"properties": {
@ -38,7 +38,7 @@
"type": "Microsoft.Network/publicIPAddresses"
},
{
"apiVersion": "[variables('apiVersion')]",
"apiVersion": "[variables('virtualNetworksApiVersion')]",
"location": "[variables('location')]",
"name": "[variables('virtualNetworkName')]",
"properties": {
@ -59,7 +59,7 @@
"type": "Microsoft.Network/virtualNetworks"
},
{
"apiVersion": "[variables('apiVersion')]",
"apiVersion": "[variables('networkInterfacesApiVersion')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
@ -142,9 +142,12 @@
],
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2015-06-15",
"apiVersion": "2017-03-30",
"location": "[resourceGroup().location]",
"managedDiskApiVersion": "2017-03-30",
"networkInterfacesApiVersion": "2017-04-01",
"nicName": "packerNic",
"publicIPAddressApiVersion": "2017-04-01",
"publicIPAddressName": "packerPublicIP",
"publicIPAddressType": "Dynamic",
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
@ -153,6 +156,7 @@
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"virtualNetworkName": "packerNetwork",
"virtualNetworkResourceGroup": "[resourceGroup().name]",
"virtualNetworksApiVersion": "2017-04-01",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
}

View File

@ -26,7 +26,7 @@
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"apiVersion": "[variables('networkInterfacesApiVersion')]",
"dependsOn": [],
"location": "[variables('location')]",
"name": "[variables('nicName')]",
@ -103,9 +103,12 @@
],
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2015-06-15",
"apiVersion": "2017-03-30",
"location": "[resourceGroup().location]",
"managedDiskApiVersion": "2017-03-30",
"networkInterfacesApiVersion": "2017-04-01",
"nicName": "packerNic",
"publicIPAddressApiVersion": "2017-04-01",
"publicIPAddressName": "packerPublicIP",
"publicIPAddressType": "Dynamic",
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
@ -114,6 +117,7 @@
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"virtualNetworkName": "virtualNetworkName",
"virtualNetworkResourceGroup": "virtualNetworkResourceGroupName",
"virtualNetworksApiVersion": "2017-04-01",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
}

View File

@ -26,7 +26,7 @@
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"apiVersion": "[variables('publicIPAddressApiVersion')]",
"location": "[variables('location')]",
"name": "[variables('publicIPAddressName')]",
"properties": {
@ -43,7 +43,7 @@
"type": "Microsoft.Network/publicIPAddresses"
},
{
"apiVersion": "[variables('apiVersion')]",
"apiVersion": "[variables('virtualNetworksApiVersion')]",
"location": "[variables('location')]",
"name": "[variables('virtualNetworkName')]",
"properties": {
@ -69,7 +69,7 @@
"type": "Microsoft.Network/virtualNetworks"
},
{
"apiVersion": "[variables('apiVersion')]",
"apiVersion": "[variables('networkInterfacesApiVersion')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
@ -162,9 +162,12 @@
],
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2015-06-15",
"apiVersion": "2017-03-30",
"location": "[resourceGroup().location]",
"managedDiskApiVersion": "2017-03-30",
"networkInterfacesApiVersion": "2017-04-01",
"nicName": "packerNic",
"publicIPAddressApiVersion": "2017-04-01",
"publicIPAddressName": "packerPublicIP",
"publicIPAddressType": "Dynamic",
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
@ -173,6 +176,7 @@
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"virtualNetworkName": "packerNetwork",
"virtualNetworkResourceGroup": "[resourceGroup().name]",
"virtualNetworksApiVersion": "2017-04-01",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
}

View File

@ -26,7 +26,7 @@
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"apiVersion": "[variables('publicIPAddressApiVersion')]",
"location": "[variables('location')]",
"name": "[variables('publicIPAddressName')]",
"properties": {
@ -38,7 +38,7 @@
"type": "Microsoft.Network/publicIPAddresses"
},
{
"apiVersion": "[variables('apiVersion')]",
"apiVersion": "[variables('virtualNetworksApiVersion')]",
"location": "[variables('location')]",
"name": "[variables('virtualNetworkName')]",
"properties": {
@ -59,7 +59,7 @@
"type": "Microsoft.Network/virtualNetworks"
},
{
"apiVersion": "[variables('apiVersion')]",
"apiVersion": "[variables('networkInterfacesApiVersion')]",
"dependsOn": [
"[concat('Microsoft.Network/publicIPAddresses/', variables('publicIPAddressName'))]",
"[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]"
@ -143,9 +143,12 @@
],
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2015-06-15",
"apiVersion": "2017-03-30",
"location": "[resourceGroup().location]",
"managedDiskApiVersion": "2017-03-30",
"networkInterfacesApiVersion": "2017-04-01",
"nicName": "packerNic",
"publicIPAddressApiVersion": "2017-04-01",
"publicIPAddressName": "packerPublicIP",
"publicIPAddressType": "Dynamic",
"sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]",
@ -154,6 +157,7 @@
"subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]",
"virtualNetworkName": "packerNetwork",
"virtualNetworkResourceGroup": "[resourceGroup().name]",
"virtualNetworksApiVersion": "2017-04-01",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
}

View File

@ -0,0 +1,175 @@
{
"$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('publicIPAddressApiVersion')]",
"location": "[variables('location')]",
"name": "[variables('publicIPAddressName')]",
"properties": {
"dnsSettings": {
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
},
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
},
"type": "Microsoft.Network/publicIPAddresses"
},
{
"apiVersion": "[variables('virtualNetworksApiVersion')]",
"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('networkInterfacesApiVersion')]",
"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'))]",
"[concat('Microsoft.Compute/images/', 'ManagedImageName')]"
],
"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": {
"id": "[resourceId(resourceGroup().name, 'Microsoft.Compute/images', 'ManagedImageName')]"
},
"osDisk": {
"caching": "ReadWrite",
"createOption": "FromImage",
"name": "osdisk"
}
}
},
"type": "Microsoft.Compute/virtualMachines"
},
{
"apiVersion": "[variables('managedDiskApiVersion')]",
"location": "",
"name": "ManagedImageName",
"properties": {
"storageProfile": {
"osDisk": {
"blobUri": "",
"osState": "",
"osType": "Linux"
}
}
},
"type": "Microsoft.Compute/images"
}
],
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2017-03-30",
"location": "[resourceGroup().location]",
"managedDiskApiVersion": "2017-03-30",
"networkInterfacesApiVersion": "2017-04-01",
"nicName": "packerNic",
"publicIPAddressApiVersion": "2017-04-01",
"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",
"virtualNetworkResourceGroup": "[resourceGroup().name]",
"virtualNetworksApiVersion": "2017-04-01",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
}
}

View File

@ -0,0 +1,176 @@
{
"$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('publicIPAddressApiVersion')]",
"location": "[variables('location')]",
"name": "[variables('publicIPAddressName')]",
"properties": {
"dnsSettings": {
"domainNameLabel": "[parameters('dnsNameForPublicIP')]"
},
"publicIPAllocationMethod": "[variables('publicIPAddressType')]"
},
"type": "Microsoft.Network/publicIPAddresses"
},
{
"apiVersion": "[variables('virtualNetworksApiVersion')]",
"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('networkInterfacesApiVersion')]",
"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'))]",
"[concat('Microsoft.Compute/images/', 'packerManagedDisk')]"
],
"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": {
"id": "[resourceId(resourceGroup().name, 'Microsoft.Compute/images', 'packerManagedDisk')]"
},
"osDisk": {
"caching": "ReadWrite",
"createOption": "FromImage",
"name": "osdisk"
}
}
},
"type": "Microsoft.Compute/virtualMachines"
},
{
"apiVersion": "[variables('managedDiskApiVersion')]",
"location": "ignore",
"name": "packerManagedDisk",
"properties": {
"storageProfile": {
"imageReference": {
"offer": "UbuntuServer",
"publisher": "Canonical",
"sku": "16.04-LTS",
"version": "--version--"
}
}
},
"type": "Microsoft.Compute/images"
}
],
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2017-03-30",
"location": "[resourceGroup().location]",
"managedDiskApiVersion": "2017-03-30",
"networkInterfacesApiVersion": "2017-04-01",
"nicName": "packerNic",
"publicIPAddressApiVersion": "2017-04-01",
"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",
"virtualNetworkResourceGroup": "[resourceGroup().name]",
"virtualNetworksApiVersion": "2017-04-01",
"vmStorageAccountContainerName": "images",
"vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]"
}
}

View File

@ -258,6 +258,74 @@ growpart:
}
}
// Ensure the VM template is correct when building from a custom managed image.
func TestVirtualMachineDeployment08(t *testing.T) {
config := map[string]interface{}{
"capture_name_prefix": "ignore",
"capture_container_name": "ignore",
"location": "ignore",
"resource_group_name": "ignore",
"storage_account": "ignore",
"subscription_id": "ignore",
"os_type": constants.Target_Linux,
"communicator": "none",
"managed_image_name": "ManagedImageName",
"managed_image_resource_group_name": "ManagedImageResourceGroupName",
"target_managed_image_name": "TargetManagedImageName",
"target_managed_image_resource_group_name": "TargetManagedImageResourceGroupName",
}
c, _, err := newConfig(config, getPackerConfiguration())
if err != nil {
t.Fatal(err)
}
deployment, err := GetVirtualMachineDeployment(c)
if err != nil {
t.Fatal(err)
}
err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template)
if err != nil {
t.Fatal(err)
}
}
// Ensure the VM template is correct when building from a platform managed image.
func TestVirtualMachineDeployment09(t *testing.T) {
config := map[string]interface{}{
"capture_name_prefix": "ignore",
"capture_container_name": "ignore",
"location": "ignore",
"resource_group_name": "ignore",
"storage_account": "ignore",
"subscription_id": "ignore",
"os_type": constants.Target_Linux,
"communicator": "none",
"image_publisher": "Canonical",
"image_offer": "UbuntuServer",
"image_sku": "16.04-LTS",
"image_version": "--version--",
"target_managed_image_name": "TargetManagedImageName",
"target_managed_image_resource_group_name": "TargetManagedImageResourceGroupName",
}
c, _, err := newConfig(config, getPackerConfiguration())
if err != nil {
t.Fatal(err)
}
deployment, err := GetVirtualMachineDeployment(c)
if err != nil {
t.Fatal(err)
}
err = approvaltests.VerifyJSONStruct(t, deployment.Properties.Template)
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())

View File

@ -16,6 +16,7 @@ const (
const (
ArmCaptureTemplate string = "arm.CaptureTemplate"
ArmComputeName string = "arm.ComputeName"
ArmImageParameters string = "arm.ImageParameters"
ArmCertificateUrl string = "arm.CertificateUrl"
ArmDeploymentName string = "arm.DeploymentName"
ArmNicName string = "arm.NicName"
@ -28,4 +29,9 @@ const (
ArmStorageAccountName string = "arm.StorageAccountName"
ArmTags string = "arm.Tags"
ArmVirtualMachineCaptureParameters string = "arm.VirtualMachineCaptureParameters"
ArmIsManagedImage string = "arm.IsManagedImage"
ArmTargetManagedImageResourceGroupName string = "arm.TargetManagedImageResourceGroupName"
ArmTargetManagedImageLocation string = "arm.TargetManagedImageLocation"
ArmTargetManagedImageName string = "arm.TargetManagedImageName"
)

View File

@ -9,11 +9,11 @@ import (
"github.com/Azure/azure-sdk-for-go/arm/resources/subscriptions"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/to"
"github.com/hashicorp/packer/version"
"github.com/mitchellh/go-homedir"
"github.com/Azure/go-autorest/autorest/adal"
)
var (

View File

@ -14,6 +14,7 @@ const (
jsonIndent = " "
resourceKeyVaults = "Microsoft.KeyVault/vaults"
resourceManagedDisk = "Microsoft.Compute/images"
resourceNetworkInterfaces = "Microsoft.Network/networkInterfaces"
resourcePublicIPAddresses = "Microsoft.Network/publicIPAddresses"
resourceVirtualMachine = "Microsoft.Compute/virtualMachines"
@ -24,6 +25,7 @@ const (
type TemplateBuilder struct {
template *Template
osType compute.OperatingSystemTypes
}
func NewTemplateBuilder(template string) (*TemplateBuilder, error) {
@ -57,6 +59,7 @@ func (s *TemplateBuilder) BuildLinux(sshAuthorizedKey string) error {
},
}
s.osType = compute.Linux
return nil
}
@ -93,6 +96,80 @@ func (s *TemplateBuilder) BuildWindows(keyVaultName, winRMCertificateUrl string)
},
},
}
s.osType = compute.Windows
return nil
}
func (s *TemplateBuilder) SetManagedDiskUrl(managedDiskImageName, location, blobUri string, osState compute.OperatingSystemStateTypes) error {
resource, err := s.getResourceByType(resourceVirtualMachine)
if err != nil {
return err
}
resourceId := s.toResourceID(resourceManagedDisk, managedDiskImageName)
profile := resource.Properties.StorageProfile
profile.ImageReference = &compute.ImageReference{
ID: to.StringPtr(resourceId),
}
profile.OsDisk.Vhd = nil
*resource.DependsOn = append(*resource.DependsOn, fmt.Sprintf("[concat('%s/', '%s')]", resourceManagedDisk, managedDiskImageName))
managedDiskResource := &Resource{
Type: to.StringPtr(resourceManagedDisk),
ApiVersion: to.StringPtr(s.toVariable("managedDiskApiVersion")),
Name: to.StringPtr(managedDiskImageName),
Location: to.StringPtr(location),
Properties: &Properties{
StorageProfile: &compute.StorageProfile{
OsDisk: &compute.OSDisk{
OsType: s.osType,
OsState: to.StringPtr(fmt.Sprintf("%s", osState)),
BlobUri: to.StringPtr(blobUri),
},
},
},
}
*s.template.Resources = append(*s.template.Resources, *managedDiskResource)
return nil
}
func (s *TemplateBuilder) SetManagedMarketplaceImage(location, publisher, offer, sku, version string) error {
resource, err := s.getResourceByType(resourceVirtualMachine)
if err != nil {
return err
}
managedDiskImageName := "packerManagedDisk"
resourceId := s.toResourceID(resourceManagedDisk, managedDiskImageName)
profile := resource.Properties.StorageProfile
profile.ImageReference = &compute.ImageReference{
ID: to.StringPtr(resourceId),
}
profile.OsDisk.Vhd = nil
*resource.DependsOn = append(*resource.DependsOn, fmt.Sprintf("[concat('%s/', '%s')]", resourceManagedDisk, managedDiskImageName))
managedDiskResource := &Resource{
Type: to.StringPtr(resourceManagedDisk),
Name: &managedDiskImageName,
ApiVersion: to.StringPtr(s.toVariable("managedDiskApiVersion")),
Location: to.StringPtr(location),
Properties: &Properties{
StorageProfile: &compute.StorageProfile{
ImageReference: &compute.ImageReference{
Publisher: &publisher,
Offer: &offer,
Sku: &sku,
Version: &version,
},
},
},
}
*s.template.Resources = append(*s.template.Resources, *managedDiskResource)
return nil
}
@ -347,7 +424,11 @@ const BasicTemplate = `{
},
"variables": {
"addressPrefix": "10.0.0.0/16",
"apiVersion": "2015-06-15",
"apiVersion": "2017-03-30",
"managedDiskApiVersion": "2017-03-30",
"networkInterfacesApiVersion": "2017-04-01",
"publicIPAddressApiVersion": "2017-04-01",
"virtualNetworksApiVersion": "2017-04-01",
"location": "[resourceGroup().location]",
"nicName": "packerNic",
"publicIPAddressName": "packerPublicIP",
@ -363,7 +444,7 @@ const BasicTemplate = `{
},
"resources": [
{
"apiVersion": "[variables('apiVersion')]",
"apiVersion": "[variables('publicIPAddressApiVersion')]",
"type": "Microsoft.Network/publicIPAddresses",
"name": "[variables('publicIPAddressName')]",
"location": "[variables('location')]",
@ -375,7 +456,7 @@ const BasicTemplate = `{
}
},
{
"apiVersion": "[variables('apiVersion')]",
"apiVersion": "[variables('virtualNetworksApiVersion')]",
"type": "Microsoft.Network/virtualNetworks",
"name": "[variables('virtualNetworkName')]",
"location": "[variables('location')]",
@ -396,7 +477,7 @@ const BasicTemplate = `{
}
},
{
"apiVersion": "[variables('apiVersion')]",
"apiVersion": "[variables('networkInterfacesApiVersion')]",
"type": "Microsoft.Network/networkInterfaces",
"name": "[variables('nicName')]",
"location": "[variables('location')]",

View File

@ -400,19 +400,19 @@ type APIErrorBase struct {
// AvailabilitySet is create or update availability set parameters.
type AvailabilitySet struct {
autorest.Response `json:"-"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
*AvailabilitySetProperties `json:"properties,omitempty"`
Sku *Sku `json:"sku,omitempty"`
Sku *Sku `json:"sku,omitempty"`
}
// AvailabilitySetListResult is the List Availability Set operation response.
type AvailabilitySetListResult struct {
autorest.Response `json:"-"`
Value *[]AvailabilitySet `json:"value,omitempty"`
Value *[]AvailabilitySet `json:"value,omitempty"`
}
// AvailabilitySetProperties is the instance view of a resource.
@ -480,11 +480,11 @@ type HardwareProfile struct {
// Image is describes an Image.
type Image struct {
autorest.Response `json:"-"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
*ImageProperties `json:"properties,omitempty"`
}
@ -501,8 +501,8 @@ type ImageDataDisk struct {
// ImageListResult is the List Image operation response.
type ImageListResult struct {
autorest.Response `json:"-"`
Value *[]Image `json:"value,omitempty"`
NextLink *string `json:"nextLink,omitempty"`
Value *[]Image `json:"value,omitempty"`
NextLink *string `json:"nextLink,omitempty"`
}
// ImageListResultPreparer prepares a request to retrieve the next set of results. It returns
@ -586,8 +586,8 @@ type LinuxConfiguration struct {
// ListUsagesResult is the List Usages operation response.
type ListUsagesResult struct {
autorest.Response `json:"-"`
Value *[]Usage `json:"value,omitempty"`
NextLink *string `json:"nextLink,omitempty"`
Value *[]Usage `json:"value,omitempty"`
NextLink *string `json:"nextLink,omitempty"`
}
// ListUsagesResultPreparer prepares a request to retrieve the next set of results. It returns
@ -605,13 +605,13 @@ func (client ListUsagesResult) ListUsagesResultPreparer() (*http.Request, error)
// ListVirtualMachineExtensionImage is
type ListVirtualMachineExtensionImage struct {
autorest.Response `json:"-"`
Value *[]VirtualMachineExtensionImage `json:"value,omitempty"`
Value *[]VirtualMachineExtensionImage `json:"value,omitempty"`
}
// ListVirtualMachineImageResource is
type ListVirtualMachineImageResource struct {
autorest.Response `json:"-"`
Value *[]VirtualMachineImageResource `json:"value,omitempty"`
Value *[]VirtualMachineImageResource `json:"value,omitempty"`
}
// LongRunningOperationProperties is compute-specific operation properties,
@ -628,7 +628,7 @@ type ManagedDiskParameters struct {
// NetworkInterfaceReference is describes a network interface reference.
type NetworkInterfaceReference struct {
ID *string `json:"id,omitempty"`
ID *string `json:"id,omitempty"`
*NetworkInterfaceReferenceProperties `json:"properties,omitempty"`
}
@ -646,16 +646,19 @@ type NetworkProfile struct {
// OperationStatusResponse is operation status response
type OperationStatusResponse struct {
autorest.Response `json:"-"`
Name *string `json:"name,omitempty"`
Status *string `json:"status,omitempty"`
StartTime *date.Time `json:"startTime,omitempty"`
EndTime *date.Time `json:"endTime,omitempty"`
Error *APIError `json:"error,omitempty"`
Name *string `json:"name,omitempty"`
Status *string `json:"status,omitempty"`
StartTime *date.Time `json:"startTime,omitempty"`
EndTime *date.Time `json:"endTime,omitempty"`
Error *APIError `json:"error,omitempty"`
}
// OSDisk is describes an Operating System disk.
type OSDisk struct {
OsType OperatingSystemTypes `json:"osType,omitempty"`
OsState *string `json:"osState,omitempty"`
StorageAccountType *string `json:"storageAccountType,omitempty"`
BlobUri *string `json:"blobUri,omitempty"`
EncryptionSettings *DiskEncryptionSettings `json:"encryptionSettings,omitempty"`
Name *string `json:"name,omitempty"`
Vhd *VirtualHardDisk `json:"vhd,omitempty"`
@ -784,15 +787,15 @@ type VirtualHardDisk struct {
// VirtualMachine is describes a Virtual Machine.
type VirtualMachine struct {
autorest.Response `json:"-"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
Plan *Plan `json:"plan,omitempty"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
Plan *Plan `json:"plan,omitempty"`
*VirtualMachineProperties `json:"properties,omitempty"`
Resources *[]VirtualMachineExtension `json:"resources,omitempty"`
Identity *VirtualMachineIdentity `json:"identity,omitempty"`
Resources *[]VirtualMachineExtension `json:"resources,omitempty"`
Identity *VirtualMachineIdentity `json:"identity,omitempty"`
}
// VirtualMachineAgentInstanceView is the instance view of the VM Agent running
@ -813,7 +816,7 @@ type VirtualMachineCaptureParameters struct {
// VirtualMachineCaptureResult is resource Id.
type VirtualMachineCaptureResult struct {
autorest.Response `json:"-"`
ID *string `json:"id,omitempty"`
ID *string `json:"id,omitempty"`
*VirtualMachineCaptureResultProperties `json:"properties,omitempty"`
}
@ -826,11 +829,11 @@ type VirtualMachineCaptureResultProperties struct {
// VirtualMachineExtension is describes a Virtual Machine Extension.
type VirtualMachineExtension struct {
autorest.Response `json:"-"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
*VirtualMachineExtensionProperties `json:"properties,omitempty"`
}
@ -845,11 +848,11 @@ type VirtualMachineExtensionHandlerInstanceView struct {
// VirtualMachineExtensionImage is describes a Virtual Machine Extension Image.
type VirtualMachineExtensionImage struct {
autorest.Response `json:"-"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
*VirtualMachineExtensionImageProperties `json:"properties,omitempty"`
}
@ -897,10 +900,10 @@ type VirtualMachineIdentity struct {
// VirtualMachineImage is describes a Virtual Machine Image.
type VirtualMachineImage struct {
autorest.Response `json:"-"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
*VirtualMachineImageProperties `json:"properties,omitempty"`
}
@ -935,8 +938,8 @@ type VirtualMachineInstanceView struct {
// VirtualMachineListResult is the List Virtual Machine operation response.
type VirtualMachineListResult struct {
autorest.Response `json:"-"`
Value *[]VirtualMachine `json:"value,omitempty"`
NextLink *string `json:"nextLink,omitempty"`
Value *[]VirtualMachine `json:"value,omitempty"`
NextLink *string `json:"nextLink,omitempty"`
}
// VirtualMachineListResultPreparer prepares a request to retrieve the next set of results. It returns
@ -968,15 +971,15 @@ type VirtualMachineProperties struct {
// VirtualMachineScaleSet is describes a Virtual Machine Scale Set.
type VirtualMachineScaleSet struct {
autorest.Response `json:"-"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
Sku *Sku `json:"sku,omitempty"`
Plan *Plan `json:"plan,omitempty"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
Sku *Sku `json:"sku,omitempty"`
Plan *Plan `json:"plan,omitempty"`
*VirtualMachineScaleSetProperties `json:"properties,omitempty"`
Identity *VirtualMachineScaleSetIdentity `json:"identity,omitempty"`
Identity *VirtualMachineScaleSetIdentity `json:"identity,omitempty"`
}
// VirtualMachineScaleSetDataDisk is describes a virtual machine scale set data
@ -993,8 +996,8 @@ type VirtualMachineScaleSetDataDisk struct {
// VirtualMachineScaleSetExtension is describes a Virtual Machine Scale Set
// Extension.
type VirtualMachineScaleSetExtension struct {
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
*VirtualMachineScaleSetExtensionProperties `json:"properties,omitempty"`
}
@ -1028,9 +1031,9 @@ type VirtualMachineScaleSetIdentity struct {
// scale set.
type VirtualMachineScaleSetInstanceView struct {
autorest.Response `json:"-"`
VirtualMachine *VirtualMachineScaleSetInstanceViewStatusesSummary `json:"virtualMachine,omitempty"`
Extensions *[]VirtualMachineScaleSetVMExtensionsSummary `json:"extensions,omitempty"`
Statuses *[]InstanceViewStatus `json:"statuses,omitempty"`
VirtualMachine *VirtualMachineScaleSetInstanceViewStatusesSummary `json:"virtualMachine,omitempty"`
Extensions *[]VirtualMachineScaleSetVMExtensionsSummary `json:"extensions,omitempty"`
Statuses *[]InstanceViewStatus `json:"statuses,omitempty"`
}
// VirtualMachineScaleSetInstanceViewStatusesSummary is instance view statuses
@ -1042,8 +1045,8 @@ type VirtualMachineScaleSetInstanceViewStatusesSummary struct {
// VirtualMachineScaleSetIPConfiguration is describes a virtual machine scale
// set network profile's IP configuration.
type VirtualMachineScaleSetIPConfiguration struct {
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
*VirtualMachineScaleSetIPConfigurationProperties `json:"properties,omitempty"`
}
@ -1060,8 +1063,8 @@ type VirtualMachineScaleSetIPConfigurationProperties struct {
// response.
type VirtualMachineScaleSetListResult struct {
autorest.Response `json:"-"`
Value *[]VirtualMachineScaleSet `json:"value,omitempty"`
NextLink *string `json:"nextLink,omitempty"`
Value *[]VirtualMachineScaleSet `json:"value,omitempty"`
NextLink *string `json:"nextLink,omitempty"`
}
// VirtualMachineScaleSetListResultPreparer prepares a request to retrieve the next set of results. It returns
@ -1080,8 +1083,8 @@ func (client VirtualMachineScaleSetListResult) VirtualMachineScaleSetListResultP
// Skus operation response.
type VirtualMachineScaleSetListSkusResult struct {
autorest.Response `json:"-"`
Value *[]VirtualMachineScaleSetSku `json:"value,omitempty"`
NextLink *string `json:"nextLink,omitempty"`
Value *[]VirtualMachineScaleSetSku `json:"value,omitempty"`
NextLink *string `json:"nextLink,omitempty"`
}
// VirtualMachineScaleSetListSkusResultPreparer prepares a request to retrieve the next set of results. It returns
@ -1100,8 +1103,8 @@ func (client VirtualMachineScaleSetListSkusResult) VirtualMachineScaleSetListSku
// operation response.
type VirtualMachineScaleSetListWithLinkResult struct {
autorest.Response `json:"-"`
Value *[]VirtualMachineScaleSet `json:"value,omitempty"`
NextLink *string `json:"nextLink,omitempty"`
Value *[]VirtualMachineScaleSet `json:"value,omitempty"`
NextLink *string `json:"nextLink,omitempty"`
}
// VirtualMachineScaleSetListWithLinkResultPreparer prepares a request to retrieve the next set of results. It returns
@ -1125,8 +1128,8 @@ type VirtualMachineScaleSetManagedDiskParameters struct {
// VirtualMachineScaleSetNetworkConfiguration is describes a virtual machine
// scale set network profile's network configurations.
type VirtualMachineScaleSetNetworkConfiguration struct {
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
*VirtualMachineScaleSetNetworkConfigurationProperties `json:"properties,omitempty"`
}
@ -1205,16 +1208,16 @@ type VirtualMachineScaleSetStorageProfile struct {
// machine.
type VirtualMachineScaleSetVM struct {
autorest.Response `json:"-"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
InstanceID *string `json:"instanceId,omitempty"`
Sku *Sku `json:"sku,omitempty"`
ID *string `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
Location *string `json:"location,omitempty"`
Tags *map[string]*string `json:"tags,omitempty"`
InstanceID *string `json:"instanceId,omitempty"`
Sku *Sku `json:"sku,omitempty"`
*VirtualMachineScaleSetVMProperties `json:"properties,omitempty"`
Plan *Plan `json:"plan,omitempty"`
Resources *[]VirtualMachineExtension `json:"resources,omitempty"`
Plan *Plan `json:"plan,omitempty"`
Resources *[]VirtualMachineExtension `json:"resources,omitempty"`
}
// VirtualMachineScaleSetVMExtensionsSummary is extensions summary for virtual
@ -1255,8 +1258,8 @@ type VirtualMachineScaleSetVMInstanceView struct {
// operation response.
type VirtualMachineScaleSetVMListResult struct {
autorest.Response `json:"-"`
Value *[]VirtualMachineScaleSetVM `json:"value,omitempty"`
NextLink *string `json:"nextLink,omitempty"`
Value *[]VirtualMachineScaleSetVM `json:"value,omitempty"`
NextLink *string `json:"nextLink,omitempty"`
}
// VirtualMachineScaleSetVMListResultPreparer prepares a request to retrieve the next set of results. It returns
@ -1309,7 +1312,7 @@ type VirtualMachineSize struct {
// VirtualMachineSizeListResult is the List Virtual Machine operation response.
type VirtualMachineSizeListResult struct {
autorest.Response `json:"-"`
Value *[]VirtualMachineSize `json:"value,omitempty"`
Value *[]VirtualMachineSize `json:"value,omitempty"`
}
// VirtualMachineStatusCodeCount is the status code and count of the virtual