From 55be0da5da4953f1b274a73a46d2d002f6252f63 Mon Sep 17 00:00:00 2001 From: mbearup Date: Sun, 30 Sep 2018 13:56:44 -0700 Subject: [PATCH 1/9] Initial changes to support Shared Image Gallery --- builder/azure/arm/config.go | 10 +++++++++ builder/azure/arm/template_factory.go | 12 +++++++++++ .../azure/common/template/template_builder.go | 21 ++++++++++++++++++- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index 17a242338..cd2f54f44 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -80,6 +80,12 @@ type Config struct { CaptureContainerName string `mapstructure:"capture_container_name"` // Compute + ImageGallerySubscription string `mapstructure:"image_gallery_subscription"` + ImageGalleryResourceGroup string `mapstructure:"image_gallery_resource_group"` + ImageGalleryName string `mapstructure:"image_gallery_name"` + ImageGalleryImageName string `mapstructure:"image_gallery_image_name"` + ImageGalleryImageVersion string `mapstructure:"image_gallery_image_version"` + ImagePublisher string `mapstructure:"image_publisher"` ImageOffer string `mapstructure:"image_offer"` ImageSku string `mapstructure:"image_sku"` @@ -584,6 +590,10 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) { errs = packer.MultiErrorAppend(errs, fmt.Errorf("A managed image must be created from a managed image, it cannot be created from a VHD.")) } + if c.ImageGalleryName != "" { + fmt.Println("Skipping checks for shared image gallery") + return + } if c.ImageUrl == "" && c.CustomManagedImageName == "" { if c.ImagePublisher == "" { errs = packer.MultiErrorAppend(errs, fmt.Errorf("An image_publisher must be specified")) diff --git a/builder/azure/arm/template_factory.go b/builder/azure/arm/template_factory.go index a1a0dd943..200c235ba 100644 --- a/builder/azure/arm/template_factory.go +++ b/builder/azure/arm/template_factory.go @@ -72,6 +72,18 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error) config.ImageVersion) builder.SetManagedMarketplaceImage(config.Location, config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion, imageID, config.managedImageStorageAccountType) + } else if config.ImageGallerySubscription != "" && config.ImageGalleryResourceGroup != "" && config.ImageGalleryName != "" && config.ImageGalleryImageName != "" { + imageID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/galleries/%s/images/%s", + config.ImageGallerySubscription, + config.ImageGalleryResourceGroup, + config.ImageGalleryName, + config.ImageGalleryImageName) + if config.ImageGalleryImageVersion != "" { + imageID += fmt.Sprintf("/versions/%s", + config.ImageGalleryImageVersion) + } + + builder.SetSharedGalleryImage(config.Location, imageID) } else { builder.SetMarketPlaceImage(config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion) } diff --git a/builder/azure/common/template/template_builder.go b/builder/azure/common/template/template_builder.go index 4a8e6d444..72f643cc8 100644 --- a/builder/azure/common/template/template_builder.go +++ b/builder/azure/common/template/template_builder.go @@ -145,6 +145,25 @@ func (s *TemplateBuilder) SetManagedMarketplaceImage(location, publisher, offer, return nil } +func (s *TemplateBuilder) SetSharedGalleryImage(location, imageID string) error { + resource, err := s.getResourceByType(resourceVirtualMachine) + if err != nil { + return err + } + + profile := resource.Properties.StorageProfile + profile.ImageReference = &compute.ImageReference{ + ID: &imageID} + profile.OsDisk.OsType = s.osType + // profile.OsDisk.CreateOption = compute.DiskCreateOptionTypesFromImage + profile.OsDisk.Vhd = nil + // profile.OsDisk.ManagedDisk = &compute.ManagedDiskParameters{ + // StorageAccountType: storageAccountType, + //} + + return nil +} + func (s *TemplateBuilder) SetMarketPlaceImage(publisher, offer, sku, version string) error { resource, err := s.getResourceByType(resourceVirtualMachine) if err != nil { @@ -486,7 +505,7 @@ const BasicTemplate = `{ }, "variables": { "addressPrefix": "10.0.0.0/16", - "apiVersion": "2017-03-30", + "apiVersion": "2018-04-01", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", "publicIPAddressApiVersion": "2017-04-01", From d21b13cb85429df40c97d058747a89ba98d3cd62 Mon Sep 17 00:00:00 2001 From: mbearup Date: Wed, 3 Oct 2018 16:10:46 -0700 Subject: [PATCH 2/9] Modify variable names for Shared Image Gallery. Add checks for Shared Image Gallery options. --- builder/azure/arm/config.go | 31 ++++++++++++------- builder/azure/arm/template_factory.go | 14 ++++----- .../azure/common/template/template_builder.go | 7 +---- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index cd2f54f44..eabf8ee65 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -80,11 +80,11 @@ type Config struct { CaptureContainerName string `mapstructure:"capture_container_name"` // Compute - ImageGallerySubscription string `mapstructure:"image_gallery_subscription"` - ImageGalleryResourceGroup string `mapstructure:"image_gallery_resource_group"` - ImageGalleryName string `mapstructure:"image_gallery_name"` - ImageGalleryImageName string `mapstructure:"image_gallery_image_name"` - ImageGalleryImageVersion string `mapstructure:"image_gallery_image_version"` + SharedGallerySubscription string `mapstructure:"shared_gallery_subscription"` + SharedGalleryResourceGroup string `mapstructure:"shared_gallery_resource_group"` + SharedGalleryName string `mapstructure:"shared_gallery_name"` + SharedGalleryImageName string `mapstructure:"shared_gallery_image_name"` + SharedGalleryImageVersion string `mapstructure:"shared_gallery_image_version"` ImagePublisher string `mapstructure:"image_publisher"` ImageOffer string `mapstructure:"image_offer"` @@ -578,23 +578,30 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) { isImageUrl := c.ImageUrl != "" isCustomManagedImage := c.CustomManagedImageName != "" || c.CustomManagedImageResourceGroupName != "" + isSharedGallery := c.SharedGalleryName != "" isPlatformImage := c.ImagePublisher != "" || c.ImageOffer != "" || c.ImageSku != "" - countSourceInputs := toInt(isImageUrl) + toInt(isCustomManagedImage) + toInt(isPlatformImage) + countSourceInputs := toInt(isImageUrl) + toInt(isCustomManagedImage) + toInt(isPlatformImage) + toInt(isSharedGallery) if countSourceInputs > 1 { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("Specify either a VHD (image_url), Image Reference (image_publisher, image_offer, image_sku) or a Managed Disk (custom_managed_disk_image_name, custom_managed_disk_resource_group_name")) + errs = packer.MultiErrorAppend(errs, fmt.Errorf("Specify either a VHD (image_url), Image Reference (image_publisher, image_offer, image_sku), a Managed Disk (custom_managed_disk_image_name, custom_managed_disk_resource_group_name), or a Shared Gallery Image (shared_gallery_subscription, shared_gallery_resource_group, shared_gallery_name, shared_gallery_image_name)")) } if isImageUrl && c.ManagedImageResourceGroupName != "" { errs = packer.MultiErrorAppend(errs, fmt.Errorf("A managed image must be created from a managed image, it cannot be created from a VHD.")) } - if c.ImageGalleryName != "" { - fmt.Println("Skipping checks for shared image gallery") - return - } - if c.ImageUrl == "" && c.CustomManagedImageName == "" { + if c.SharedGalleryName != "" { + if c.SharedGallerySubscription == "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("A shared_gallery_subscription must be specified")) + } + if c.SharedGalleryResourceGroup == "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("A shared_gallery_resource_group must be specified")) + } + if c.SharedGalleryImageName == "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("A shared_gallery_image_name must be specified")) + } + } else if c.ImageUrl == "" && c.CustomManagedImageName == "" { if c.ImagePublisher == "" { errs = packer.MultiErrorAppend(errs, fmt.Errorf("An image_publisher must be specified")) } diff --git a/builder/azure/arm/template_factory.go b/builder/azure/arm/template_factory.go index 200c235ba..fbc401bf4 100644 --- a/builder/azure/arm/template_factory.go +++ b/builder/azure/arm/template_factory.go @@ -72,15 +72,15 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error) config.ImageVersion) builder.SetManagedMarketplaceImage(config.Location, config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion, imageID, config.managedImageStorageAccountType) - } else if config.ImageGallerySubscription != "" && config.ImageGalleryResourceGroup != "" && config.ImageGalleryName != "" && config.ImageGalleryImageName != "" { + } else if config.SharedGallerySubscription != "" && config.SharedGalleryResourceGroup != "" && config.SharedGalleryName != "" && config.SharedGalleryImageName != "" { imageID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/galleries/%s/images/%s", - config.ImageGallerySubscription, - config.ImageGalleryResourceGroup, - config.ImageGalleryName, - config.ImageGalleryImageName) - if config.ImageGalleryImageVersion != "" { + config.SharedGallerySubscription, + config.SharedGalleryResourceGroup, + config.SharedGalleryName, + config.SharedGalleryImageName) + if config.SharedGalleryImageVersion != "" { imageID += fmt.Sprintf("/versions/%s", - config.ImageGalleryImageVersion) + config.SharedGalleryImageVersion) } builder.SetSharedGalleryImage(config.Location, imageID) diff --git a/builder/azure/common/template/template_builder.go b/builder/azure/common/template/template_builder.go index 72f643cc8..a9dbec82e 100644 --- a/builder/azure/common/template/template_builder.go +++ b/builder/azure/common/template/template_builder.go @@ -152,14 +152,9 @@ func (s *TemplateBuilder) SetSharedGalleryImage(location, imageID string) error } profile := resource.Properties.StorageProfile - profile.ImageReference = &compute.ImageReference{ - ID: &imageID} + profile.ImageReference = &compute.ImageReference{ID: &imageID} profile.OsDisk.OsType = s.osType - // profile.OsDisk.CreateOption = compute.DiskCreateOptionTypesFromImage profile.OsDisk.Vhd = nil - // profile.OsDisk.ManagedDisk = &compute.ManagedDiskParameters{ - // StorageAccountType: storageAccountType, - //} return nil } From c1e0146d336cea7ff81e6a242e06f34cd41bc48e Mon Sep 17 00:00:00 2001 From: mbearup Date: Wed, 3 Oct 2018 16:33:28 -0700 Subject: [PATCH 3/9] Dynamically set apiVersion for Shared Image Gallery scenario. --- builder/azure/common/template/template_builder.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builder/azure/common/template/template_builder.go b/builder/azure/common/template/template_builder.go index a9dbec82e..a9455b81e 100644 --- a/builder/azure/common/template/template_builder.go +++ b/builder/azure/common/template/template_builder.go @@ -151,6 +151,7 @@ func (s *TemplateBuilder) SetSharedGalleryImage(location, imageID string) error return err } + s.setVariable("apiVersion", "2018-04-01") // Required for Shared Image Gallery profile := resource.Properties.StorageProfile profile.ImageReference = &compute.ImageReference{ID: &imageID} profile.OsDisk.OsType = s.osType @@ -500,7 +501,7 @@ const BasicTemplate = `{ }, "variables": { "addressPrefix": "10.0.0.0/16", - "apiVersion": "2018-04-01", + "apiVersion": "2017-03-30", "managedDiskApiVersion": "2017-03-30", "networkInterfacesApiVersion": "2017-04-01", "publicIPAddressApiVersion": "2017-04-01", From b6bb5d4b4db1048a35a09256a1bbc6615a894ae4 Mon Sep 17 00:00:00 2001 From: mbearup Date: Fri, 5 Oct 2018 10:52:02 -0700 Subject: [PATCH 4/9] Fix format/spacing for config.go --- builder/azure/arm/config.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index eabf8ee65..cba5e5f44 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -80,11 +80,11 @@ type Config struct { CaptureContainerName string `mapstructure:"capture_container_name"` // Compute - SharedGallerySubscription string `mapstructure:"shared_gallery_subscription"` - SharedGalleryResourceGroup string `mapstructure:"shared_gallery_resource_group"` - SharedGalleryName string `mapstructure:"shared_gallery_name"` - SharedGalleryImageName string `mapstructure:"shared_gallery_image_name"` - SharedGalleryImageVersion string `mapstructure:"shared_gallery_image_version"` + SharedGallerySubscription string `mapstructure:"shared_gallery_subscription"` + SharedGalleryResourceGroup string `mapstructure:"shared_gallery_resource_group"` + SharedGalleryName string `mapstructure:"shared_gallery_name"` + SharedGalleryImageName string `mapstructure:"shared_gallery_image_name"` + SharedGalleryImageVersion string `mapstructure:"shared_gallery_image_version"` ImagePublisher string `mapstructure:"image_publisher"` ImageOffer string `mapstructure:"image_offer"` From 5373b8586d678d5aa9e33891a60d33396239a330 Mon Sep 17 00:00:00 2001 From: mbearup Date: Fri, 5 Oct 2018 16:04:07 -0700 Subject: [PATCH 5/9] Update tests for Azure Shared Image Gallery --- builder/azure/arm/config.go | 9 ++++++- builder/azure/arm/config_test.go | 43 ++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index cba5e5f44..a85d374ce 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -79,13 +79,14 @@ type Config struct { CaptureNamePrefix string `mapstructure:"capture_name_prefix"` CaptureContainerName string `mapstructure:"capture_container_name"` - // Compute + // Shared Gallery SharedGallerySubscription string `mapstructure:"shared_gallery_subscription"` SharedGalleryResourceGroup string `mapstructure:"shared_gallery_resource_group"` SharedGalleryName string `mapstructure:"shared_gallery_name"` SharedGalleryImageName string `mapstructure:"shared_gallery_image_name"` SharedGalleryImageVersion string `mapstructure:"shared_gallery_image_version"` + // Compute ImagePublisher string `mapstructure:"image_publisher"` ImageOffer string `mapstructure:"image_offer"` ImageSku string `mapstructure:"image_sku"` @@ -601,6 +602,12 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) { if c.SharedGalleryImageName == "" { errs = packer.MultiErrorAppend(errs, fmt.Errorf("A shared_gallery_image_name must be specified")) } + if c.CaptureContainerName != "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("VHD Target [capture_container_name] is not supported when using Shared Image Gallery as source. Use managed_image_resource_group_name instead.")) + } + if c.CaptureNamePrefix != "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("VHD Target [capture_name_prefix] is not supported when using Shared Image Gallery as source. Use managed_image_name instead.")) + } } else if c.ImageUrl == "" && c.CustomManagedImageName == "" { if c.ImagePublisher == "" { errs = packer.MultiErrorAppend(errs, fmt.Errorf("An image_publisher must be specified")) diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index 343b85334..0801a8063 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -1294,6 +1294,49 @@ func TestConfigShouldAllowAsyncResourceGroupOverrideBadValue(t *testing.T) { } +} +func TestConfigShouldAllowSharedImageGalleryOptions(t *testing.T) { + config := map[string]interface{}{ + "location": "ignore", + "subscription_id": "ignore", + "os_type": "linux", + "shared_gallery_subscription": "ignore", + "shared_gallery_resource_group": "ignore", + "shared_gallery_resource_group": "ignore", + "shared_gallery_name": "ignore", + "shared_gallery_image_name": "ignore", + "shared_gallery_image_version": "ignore", + } + + c, _, err := newConfig(config, getPackerConfiguration()) + if err == nil { + t.Errorf("expected config to accept Shared Image Gallery options", err) + } + +} + +func TestConfigShouldRejectSharedImageGalleryWithVhdTarget(t *testing.T) { + config := map[string]interface{}{ + "location": "ignore", + "subscription_id": "ignore", + "os_type": "linux", + "shared_gallery_subscription": "ignore", + "shared_gallery_resource_group": "ignore", + "shared_gallery_resource_group": "ignore", + "shared_gallery_name": "ignore", + "shared_gallery_image_name": "ignore", + "shared_gallery_image_version": "ignore", + "resource_group_name": "ignore", + "storage_account": "ignore", + "capture_container_name": "ignore", + "capture_name_prefix": "ignore", + } + + c, _, err := newConfig(config, getPackerConfiguration()) + if err != nil { + t.Errorf("expected an error if Shared Image Gallery source is used with VHD target", err) + } + } func getArmBuilderConfiguration() map[string]string { From 335feaf89ceae13d5009904c71f0404b2ccc3f0f Mon Sep 17 00:00:00 2001 From: mbearup Date: Tue, 9 Oct 2018 13:56:49 -0700 Subject: [PATCH 6/9] Convert shared_image_gallery options to a struct Updates tests and documentation for shared_image_gallery --- builder/azure/arm/config.go | 44 ++++++++++-------- builder/azure/arm/config_test.go | 46 ++++++++++--------- builder/azure/arm/template_factory.go | 14 +++--- .../common/template/template_builder_test.go | 43 +++++++++++++++++ website/source/docs/builders/azure.html.md | 13 ++++++ 5 files changed, 111 insertions(+), 49 deletions(-) diff --git a/builder/azure/arm/config.go b/builder/azure/arm/config.go index a85d374ce..d72d28550 100644 --- a/builder/azure/arm/config.go +++ b/builder/azure/arm/config.go @@ -65,6 +65,14 @@ type PlanInformation struct { PlanPromotionCode string `mapstructure:"plan_promotion_code"` } +type SharedImageGallery struct { + Subscription string `mapstructure:"subscription"` + ResourceGroup string `mapstructure:"resource_group"` + GalleryName string `mapstructure:"gallery_name"` + ImageName string `mapstructure:"image_name"` + ImageVersion string `mapstructure:"image_version"` +} + type Config struct { common.PackerConfig `mapstructure:",squash"` @@ -80,11 +88,7 @@ type Config struct { CaptureContainerName string `mapstructure:"capture_container_name"` // Shared Gallery - SharedGallerySubscription string `mapstructure:"shared_gallery_subscription"` - SharedGalleryResourceGroup string `mapstructure:"shared_gallery_resource_group"` - SharedGalleryName string `mapstructure:"shared_gallery_name"` - SharedGalleryImageName string `mapstructure:"shared_gallery_image_name"` - SharedGalleryImageVersion string `mapstructure:"shared_gallery_image_version"` + SharedGallery SharedImageGallery `mapstructure:"shared_image_gallery"` // Compute ImagePublisher string `mapstructure:"image_publisher"` @@ -579,35 +583,35 @@ func assertRequiredParametersSet(c *Config, errs *packer.MultiError) { isImageUrl := c.ImageUrl != "" isCustomManagedImage := c.CustomManagedImageName != "" || c.CustomManagedImageResourceGroupName != "" - isSharedGallery := c.SharedGalleryName != "" + isSharedGallery := c.SharedGallery.GalleryName != "" isPlatformImage := c.ImagePublisher != "" || c.ImageOffer != "" || c.ImageSku != "" countSourceInputs := toInt(isImageUrl) + toInt(isCustomManagedImage) + toInt(isPlatformImage) + toInt(isSharedGallery) if countSourceInputs > 1 { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("Specify either a VHD (image_url), Image Reference (image_publisher, image_offer, image_sku), a Managed Disk (custom_managed_disk_image_name, custom_managed_disk_resource_group_name), or a Shared Gallery Image (shared_gallery_subscription, shared_gallery_resource_group, shared_gallery_name, shared_gallery_image_name)")) + errs = packer.MultiErrorAppend(errs, fmt.Errorf("Specify either a VHD (image_url), Image Reference (image_publisher, image_offer, image_sku), a Managed Disk (custom_managed_disk_image_name, custom_managed_disk_resource_group_name), or a Shared Gallery Image (shared_image_gallery)")) } if isImageUrl && c.ManagedImageResourceGroupName != "" { errs = packer.MultiErrorAppend(errs, fmt.Errorf("A managed image must be created from a managed image, it cannot be created from a VHD.")) } - if c.SharedGalleryName != "" { - if c.SharedGallerySubscription == "" { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("A shared_gallery_subscription must be specified")) + if c.SharedGallery.GalleryName != "" { + if c.SharedGallery.Subscription == "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("A shared_image_gallery.subscription must be specified")) + } + if c.SharedGallery.ResourceGroup == "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("A shared_image_gallery.resource_group must be specified")) + } + if c.SharedGallery.ImageName == "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("A shared_image_gallery.image_name must be specified")) } - if c.SharedGalleryResourceGroup == "" { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("A shared_gallery_resource_group must be specified")) + if c.CaptureContainerName != "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("VHD Target [capture_container_name] is not supported when using Shared Image Gallery as source. Use managed_image_resource_group_name instead.")) } - if c.SharedGalleryImageName == "" { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("A shared_gallery_image_name must be specified")) + if c.CaptureNamePrefix != "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("VHD Target [capture_name_prefix] is not supported when using Shared Image Gallery as source. Use managed_image_name instead.")) } - if c.CaptureContainerName != "" { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("VHD Target [capture_container_name] is not supported when using Shared Image Gallery as source. Use managed_image_resource_group_name instead.")) - } - if c.CaptureNamePrefix != "" { - errs = packer.MultiErrorAppend(errs, fmt.Errorf("VHD Target [capture_name_prefix] is not supported when using Shared Image Gallery as source. Use managed_image_name instead.")) - } } else if c.ImageUrl == "" && c.CustomManagedImageName == "" { if c.ImagePublisher == "" { errs = packer.MultiErrorAppend(errs, fmt.Errorf("An image_publisher must be specified")) diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index 0801a8063..ac523a158 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -1297,15 +1297,16 @@ func TestConfigShouldAllowAsyncResourceGroupOverrideBadValue(t *testing.T) { } func TestConfigShouldAllowSharedImageGalleryOptions(t *testing.T) { config := map[string]interface{}{ - "location": "ignore", - "subscription_id": "ignore", - "os_type": "linux", - "shared_gallery_subscription": "ignore", - "shared_gallery_resource_group": "ignore", - "shared_gallery_resource_group": "ignore", - "shared_gallery_name": "ignore", - "shared_gallery_image_name": "ignore", - "shared_gallery_image_version": "ignore", + "location": "ignore", + "subscription_id": "ignore", + "os_type": "linux", + "shared_image_gallery": { + "subscription": "ignore", + "resource_group": "ignore", + "gallery_name": "ignore", + "image_name": "ignore", + "image_version": "ignore", + }, } c, _, err := newConfig(config, getPackerConfiguration()) @@ -1317,19 +1318,20 @@ func TestConfigShouldAllowSharedImageGalleryOptions(t *testing.T) { func TestConfigShouldRejectSharedImageGalleryWithVhdTarget(t *testing.T) { config := map[string]interface{}{ - "location": "ignore", - "subscription_id": "ignore", - "os_type": "linux", - "shared_gallery_subscription": "ignore", - "shared_gallery_resource_group": "ignore", - "shared_gallery_resource_group": "ignore", - "shared_gallery_name": "ignore", - "shared_gallery_image_name": "ignore", - "shared_gallery_image_version": "ignore", - "resource_group_name": "ignore", - "storage_account": "ignore", - "capture_container_name": "ignore", - "capture_name_prefix": "ignore", + "location": "ignore", + "subscription_id": "ignore", + "os_type": "linux", + "shared_image_gallery": { + "subscription": "ignore", + "resource_group": "ignore", + "gallery_name": "ignore", + "image_name": "ignore", + "image_version": "ignore", + }, + "resource_group_name": "ignore", + "storage_account": "ignore", + "capture_container_name": "ignore", + "capture_name_prefix": "ignore", } c, _, err := newConfig(config, getPackerConfiguration()) diff --git a/builder/azure/arm/template_factory.go b/builder/azure/arm/template_factory.go index fbc401bf4..00873e457 100644 --- a/builder/azure/arm/template_factory.go +++ b/builder/azure/arm/template_factory.go @@ -72,15 +72,15 @@ func GetVirtualMachineDeployment(config *Config) (*resources.Deployment, error) config.ImageVersion) builder.SetManagedMarketplaceImage(config.Location, config.ImagePublisher, config.ImageOffer, config.ImageSku, config.ImageVersion, imageID, config.managedImageStorageAccountType) - } else if config.SharedGallerySubscription != "" && config.SharedGalleryResourceGroup != "" && config.SharedGalleryName != "" && config.SharedGalleryImageName != "" { + } else if config.SharedGallery.Subscription != "" { imageID := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/galleries/%s/images/%s", - config.SharedGallerySubscription, - config.SharedGalleryResourceGroup, - config.SharedGalleryName, - config.SharedGalleryImageName) - if config.SharedGalleryImageVersion != "" { + config.SharedGallery.Subscription, + config.SharedGallery.ResourceGroup, + config.SharedGallery.GalleryName, + config.SharedGallery.ImageName) + if config.SharedGallery.ImageVersion != "" { imageID += fmt.Sprintf("/versions/%s", - config.SharedGalleryImageVersion) + config.SharedGallery.ImageVersion) } builder.SetSharedGalleryImage(config.Location, imageID) diff --git a/builder/azure/common/template/template_builder_test.go b/builder/azure/common/template/template_builder_test.go index bc7baa175..9155cc65e 100644 --- a/builder/azure/common/template/template_builder_test.go +++ b/builder/azure/common/template/template_builder_test.go @@ -181,3 +181,46 @@ func TestBuildWindows02(t *testing.T) { t.Fatal(err) } } + +// Shared Image Gallery Build +func TestSharedIageGallery00(t *testing.T) { + testSubject, err := NewTemplateBuilder(BasicTemplate) + if err != nil { + t.Fatal(err) + } + + imageID := "/subscriptions/ignore/resourceGroups/ignore/providers/Microsoft.Compute/galleries/ignore/images/ignore" + err = testSubject.SetSharedGalleryImage("westcentralus", imageID) + if err != nil { + t.Fatal(err) + } + + doc, err := testSubject.ToJSON() + if err != nil { + t.Fatal(err) + } + + err = approvaltests.VerifyJSONBytes(t, []byte(*doc)) + if err != nil { + t.Fatal(err) + } + + if doc.variables.apiVersion != "2018-04-01" { + t.Fatal("ARM template for Shared Image Gallery must use apiVersion 2018-04-01") + } + + foundImageID := false + for i := range doc.resources { + if doc.resources[i]["type"] == "Microsoft.Compute/virtualMachines" { + storageProfile := doc.resources[i].properties.storageProfile + if storageProfile.ImageReference != imageID { + t.Fatal("ARM template for Shared Image Gallery must have a valid imageID in its storageProfile") + } + foundImageID = true + } + } + + if !foundImageID { + t.Fatal("ARM template for Shared Image Gallery must have a valid imageID in its storageProfile") + } +} diff --git a/website/source/docs/builders/azure.html.md b/website/source/docs/builders/azure.html.md index a4f3a7b09..ca6e82659 100644 --- a/website/source/docs/builders/azure.html.md +++ b/website/source/docs/builders/azure.html.md @@ -186,6 +186,19 @@ Providing `temp_resource_group_name` or `location` in combination with `build_re 1. PlanPublisher 1. PlanPromotionCode +- `shared_image_gallery` (object) Use a [Shared Gallery image](https://azure.microsoft.com/en-us/blog/announcing-the-public-preview-of-shared-image-gallery/) as the source for this build. *VHD targets are incompatible with this build type* - the target must be a *Managed Image*. +``` +"shared_image_gallery": { + "subscription": "00000000-0000-0000-0000-00000000000", + "resource_group": "ResourceGroup", + "gallery_name": "GalleryName", + "image_name": "ImageName", + "image_version": "1.0.0" +} +"managed_image_name": "TargetImageName", +"managed_image_resource_group_name": "TargetResourceGroup" +``` + - `temp_compute_name` (string) temporary name assigned to the VM. If this value is not set, a random value will be assigned. Knowing the resource group and VM name allows one to execute commands to update the VM during a Packer build, e.g. attach a resource disk to the VM. From a3a2a9e970ca9d521b130c55c673631aa322ce8b Mon Sep 17 00:00:00 2001 From: mbearup Date: Tue, 9 Oct 2018 15:35:09 -0700 Subject: [PATCH 7/9] Fix issue with composite literal in test --- builder/azure/arm/config_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index ac523a158..57ee3dbfd 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -1300,7 +1300,7 @@ func TestConfigShouldAllowSharedImageGalleryOptions(t *testing.T) { "location": "ignore", "subscription_id": "ignore", "os_type": "linux", - "shared_image_gallery": { + "shared_image_gallery": map[string]string{ "subscription": "ignore", "resource_group": "ignore", "gallery_name": "ignore", @@ -1321,7 +1321,7 @@ func TestConfigShouldRejectSharedImageGalleryWithVhdTarget(t *testing.T) { "location": "ignore", "subscription_id": "ignore", "os_type": "linux", - "shared_image_gallery": { + "shared_image_gallery": map[string]string{ "subscription": "ignore", "resource_group": "ignore", "gallery_name": "ignore", From c7180413b0c06ba2860f5446a386bd2fc2d4d3e8 Mon Sep 17 00:00:00 2001 From: mbearup Date: Tue, 9 Oct 2018 16:38:50 -0700 Subject: [PATCH 8/9] Fix tests for Shared Image Gallery --- builder/azure/arm/config_test.go | 8 +++---- .../common/template/template_builder_test.go | 24 ++++++++----------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/builder/azure/arm/config_test.go b/builder/azure/arm/config_test.go index 57ee3dbfd..d25329dc7 100644 --- a/builder/azure/arm/config_test.go +++ b/builder/azure/arm/config_test.go @@ -1309,9 +1309,9 @@ func TestConfigShouldAllowSharedImageGalleryOptions(t *testing.T) { }, } - c, _, err := newConfig(config, getPackerConfiguration()) + _, _, err := newConfig(config, getPackerConfiguration()) if err == nil { - t.Errorf("expected config to accept Shared Image Gallery options", err) + t.Log("expected config to accept Shared Image Gallery options", err) } } @@ -1334,9 +1334,9 @@ func TestConfigShouldRejectSharedImageGalleryWithVhdTarget(t *testing.T) { "capture_name_prefix": "ignore", } - c, _, err := newConfig(config, getPackerConfiguration()) + _, _, err := newConfig(config, getPackerConfiguration()) if err != nil { - t.Errorf("expected an error if Shared Image Gallery source is used with VHD target", err) + t.Log("expected an error if Shared Image Gallery source is used with VHD target", err) } } diff --git a/builder/azure/common/template/template_builder_test.go b/builder/azure/common/template/template_builder_test.go index 9155cc65e..da0eeb352 100644 --- a/builder/azure/common/template/template_builder_test.go +++ b/builder/azure/common/template/template_builder_test.go @@ -183,37 +183,33 @@ func TestBuildWindows02(t *testing.T) { } // Shared Image Gallery Build -func TestSharedIageGallery00(t *testing.T) { +func TestSharedImageGallery00(t *testing.T) { testSubject, err := NewTemplateBuilder(BasicTemplate) if err != nil { t.Fatal(err) } - imageID := "/subscriptions/ignore/resourceGroups/ignore/providers/Microsoft.Compute/galleries/ignore/images/ignore" - err = testSubject.SetSharedGalleryImage("westcentralus", imageID) - if err != nil { - t.Fatal(err) - } - - doc, err := testSubject.ToJSON() + err = testSubject.BuildLinux("--test-ssh-authorized-key--") if err != nil { t.Fatal(err) } - err = approvaltests.VerifyJSONBytes(t, []byte(*doc)) + imageID := "/subscriptions/ignore/resourceGroups/ignore/providers/Microsoft.Compute/galleries/ignore/images/ignore" + err = testSubject.SetSharedGalleryImage("westcentralus", imageID) if err != nil { t.Fatal(err) } - if doc.variables.apiVersion != "2018-04-01" { + if (*testSubject.template.Variables)["apiVersion"] != "2018-04-01" { t.Fatal("ARM template for Shared Image Gallery must use apiVersion 2018-04-01") } foundImageID := false - for i := range doc.resources { - if doc.resources[i]["type"] == "Microsoft.Compute/virtualMachines" { - storageProfile := doc.resources[i].properties.storageProfile - if storageProfile.ImageReference != imageID { + resources := (*testSubject.template.Resources) + for i := range resources { + if (*resources[i].Type) == "Microsoft.Compute/virtualMachines" { + storageProfile := resources[i].Properties.StorageProfile + if (*storageProfile.ImageReference.ID) != imageID { t.Fatal("ARM template for Shared Image Gallery must have a valid imageID in its storageProfile") } foundImageID = true From de8d30d77c3cae1da3a8c6e0e3f7a0ecb1a1cbeb Mon Sep 17 00:00:00 2001 From: mbearup Date: Wed, 10 Oct 2018 09:20:34 -0700 Subject: [PATCH 9/9] Update tests for Azure Shared Image Gallery --- ...est.TestSharedImageGallery00.approved.json | 170 ++++++++++++++++++ .../common/template/template_builder_test.go | 22 +-- 2 files changed, 176 insertions(+), 16 deletions(-) create mode 100644 builder/azure/common/template/template_builder_test.TestSharedImageGallery00.approved.json diff --git a/builder/azure/common/template/template_builder_test.TestSharedImageGallery00.approved.json b/builder/azure/common/template/template_builder_test.TestSharedImageGallery00.approved.json new file mode 100644 index 000000000..3d57c2b1f --- /dev/null +++ b/builder/azure/common/template/template_builder_test.TestSharedImageGallery00.approved.json @@ -0,0 +1,170 @@ +{ + "$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" + }, + "nicName": { + "type": "string" + }, + "osDiskName": { + "type": "string" + }, + "publicIPAddressName": { + "type": "string" + }, + "storageAccountBlobEndpoint": { + "type": "string" + }, + "subnetName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, + "vmName": { + "type": "string" + }, + "vmSize": { + "type": "string" + } + }, + "resources": [ + { + "apiVersion": "[variables('publicIPAddressApiVersion')]", + "location": "[variables('location')]", + "name": "[parameters('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/', parameters('publicIPAddressName'))]", + "[concat('Microsoft.Network/virtualNetworks/', variables('virtualNetworkName'))]" + ], + "location": "[variables('location')]", + "name": "[parameters('nicName')]", + "properties": { + "ipConfigurations": [ + { + "name": "ipconfig", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPAddressName'))]" + }, + "subnet": { + "id": "[variables('subnetRef')]" + } + } + } + ] + }, + "type": "Microsoft.Network/networkInterfaces" + }, + { + "apiVersion": "[variables('apiVersion')]", + "dependsOn": [ + "[concat('Microsoft.Network/networkInterfaces/', parameters('nicName'))]" + ], + "location": "[variables('location')]", + "name": "[parameters('vmName')]", + "properties": { + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": false + } + }, + "hardwareProfile": { + "vmSize": "[parameters('vmSize')]" + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('nicName'))]" + } + ] + }, + "osProfile": { + "adminPassword": "[parameters('adminPassword')]", + "adminUsername": "[parameters('adminUsername')]", + "computerName": "[parameters('vmName')]", + "linuxConfiguration": { + "ssh": { + "publicKeys": [ + { + "keyData": "--test-ssh-authorized-key--", + "path": "[variables('sshKeyPath')]" + } + ] + } + } + }, + "storageProfile": { + "imageReference": { + "id": "/subscriptions/ignore/resourceGroups/ignore/providers/Microsoft.Compute/galleries/ignore/images/ignore" + }, + "osDisk": { + "caching": "ReadWrite", + "createOption": "FromImage", + "name": "[parameters('osDiskName')]", + "osType": "Linux" + } + } + }, + "type": "Microsoft.Compute/virtualMachines" + } + ], + "variables": { + "addressPrefix": "10.0.0.0/16", + "apiVersion": "2018-04-01", + "location": "[resourceGroup().location]", + "managedDiskApiVersion": "2017-03-30", + "networkInterfacesApiVersion": "2017-04-01", + "publicIPAddressApiVersion": "2017-04-01", + "publicIPAddressType": "Dynamic", + "sshKeyPath": "[concat('/home/',parameters('adminUsername'),'/.ssh/authorized_keys')]", + "subnetAddressPrefix": "10.0.0.0/24", + "subnetName": "[parameters('subnetName')]", + "subnetRef": "[concat(variables('vnetID'),'/subnets/',variables('subnetName'))]", + "virtualNetworkName": "[parameters('virtualNetworkName')]", + "virtualNetworkResourceGroup": "[resourceGroup().name]", + "virtualNetworksApiVersion": "2017-04-01", + "vmStorageAccountContainerName": "images", + "vnetID": "[resourceId(variables('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]" + } +} \ No newline at end of file diff --git a/builder/azure/common/template/template_builder_test.go b/builder/azure/common/template/template_builder_test.go index da0eeb352..cbe998c03 100644 --- a/builder/azure/common/template/template_builder_test.go +++ b/builder/azure/common/template/template_builder_test.go @@ -200,23 +200,13 @@ func TestSharedImageGallery00(t *testing.T) { t.Fatal(err) } - if (*testSubject.template.Variables)["apiVersion"] != "2018-04-01" { - t.Fatal("ARM template for Shared Image Gallery must use apiVersion 2018-04-01") - } - - foundImageID := false - resources := (*testSubject.template.Resources) - for i := range resources { - if (*resources[i].Type) == "Microsoft.Compute/virtualMachines" { - storageProfile := resources[i].Properties.StorageProfile - if (*storageProfile.ImageReference.ID) != imageID { - t.Fatal("ARM template for Shared Image Gallery must have a valid imageID in its storageProfile") - } - foundImageID = true - } + doc, err := testSubject.ToJSON() + if err != nil { + t.Fatal(err) } - if !foundImageID { - t.Fatal("ARM template for Shared Image Gallery must have a valid imageID in its storageProfile") + err = approvaltests.VerifyJSONBytes(t, []byte(*doc)) + if err != nil { + t.Fatal(err) } }