diff --git a/internal/services/compute/shared_image_version_resource.go b/internal/services/compute/shared_image_version_resource.go index edd06bf961ca..361f6dbbb1fc 100644 --- a/internal/services/compute/shared_image_version_resource.go +++ b/internal/services/compute/shared_image_version_resource.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/validate" + storageValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tags" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" @@ -111,6 +112,23 @@ func resourceSharedImageVersion() *pluginsdk.Resource { }, }, + "blob_uri": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IsURLWithScheme([]string{"http", "https"}), + RequiredWith: []string{"storage_account_id"}, + ExactlyOneOf: []string{"blob_uri", "os_disk_snapshot_id", "managed_image_id"}, + }, + + "storage_account_id": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + RequiredWith: []string{"blob_uri"}, + ValidateFunc: storageValidate.StorageAccountID, + }, + "end_of_life_date": { Type: pluginsdk.TypeString, Optional: true, @@ -122,7 +140,7 @@ func resourceSharedImageVersion() *pluginsdk.Resource { Type: pluginsdk.TypeString, Optional: true, ForceNew: true, - ExactlyOneOf: []string{"os_disk_snapshot_id", "managed_image_id"}, + ExactlyOneOf: []string{"blob_uri", "os_disk_snapshot_id", "managed_image_id"}, // TODO -- add a validation function when snapshot has its own validation function }, @@ -134,7 +152,7 @@ func resourceSharedImageVersion() *pluginsdk.Resource { validate.ImageID, validate.VirtualMachineID, ), - ExactlyOneOf: []string{"os_disk_snapshot_id", "managed_image_id"}, + ExactlyOneOf: []string{"blob_uri", "os_disk_snapshot_id", "managed_image_id"}, }, "replication_mode": { @@ -225,6 +243,15 @@ func resourceSharedImageVersionCreateUpdate(d *pluginsdk.ResourceData, meta inte } } + if v, ok := d.GetOk("blob_uri"); ok { + version.GalleryImageVersionProperties.StorageProfile.OsDiskImage = &compute.GalleryOSDiskImage{ + Source: &compute.GalleryArtifactVersionSource{ + ID: utils.String(d.Get("storage_account_id").(string)), + URI: utils.String(v.(string)), + }, + } + } + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.GalleryName, id.ImageName, id.VersionName, version) if err != nil { return fmt.Errorf("creating %s: %+v", id, err) @@ -292,11 +319,24 @@ func resourceSharedImageVersionRead(d *pluginsdk.ResourceData, meta interface{}) d.Set("managed_image_id", source.ID) } + blobURI := "" + if profile.OsDiskImage != nil && profile.OsDiskImage.Source != nil && profile.OsDiskImage.Source.URI != nil { + blobURI = *profile.OsDiskImage.Source.URI + } + d.Set("blob_uri", blobURI) + osDiskSnapShotID := "" + storageAccountID := "" if profile.OsDiskImage != nil && profile.OsDiskImage.Source != nil && profile.OsDiskImage.Source.ID != nil { - osDiskSnapShotID = *profile.OsDiskImage.Source.ID + sourceID := *profile.OsDiskImage.Source.ID + if blobURI == "" { + osDiskSnapShotID = sourceID + } else { + storageAccountID = sourceID + } } d.Set("os_disk_snapshot_id", osDiskSnapShotID) + d.Set("storage_account_id", storageAccountID) } } diff --git a/internal/services/compute/shared_image_version_resource_test.go b/internal/services/compute/shared_image_version_resource_test.go index d06b1e2a899c..3dab9c7286f1 100644 --- a/internal/services/compute/shared_image_version_resource_test.go +++ b/internal/services/compute/shared_image_version_resource_test.go @@ -102,6 +102,21 @@ func TestAccSharedImageVersion_storageAccountTypeZrs(t *testing.T) { }) } +func TestAccSharedImageVersion_blobURI(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_shared_image_version", "test") + r := SharedImageVersionResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.imageVersionBlobURI(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccSharedImageVersion_specializedImageVersionBySnapshot(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_shared_image_version", "test") r := SharedImageVersionResource{} @@ -330,6 +345,49 @@ resource "azurerm_shared_image_version" "test" { `, template) } +func (r SharedImageVersionResource) imageVersionBlobURI(data acceptance.TestData) string { + template := r.setup(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_shared_image_gallery" "test" { + name = "acctestsig%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_shared_image" "test" { + name = "acctestimg%[2]d" + gallery_name = azurerm_shared_image_gallery.test.name + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + os_type = "Linux" + + identifier { + publisher = "AccTesPublisher%[2]d" + offer = "AccTesOffer%[2]d" + sku = "AccTesSku%[2]d" + } +} + +resource "azurerm_shared_image_version" "test" { + name = "0.0.1" + gallery_name = azurerm_shared_image_gallery.test.name + image_name = azurerm_shared_image.test.name + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + + blob_uri = azurerm_virtual_machine.testsource.storage_os_disk[0].vhd_uri + storage_account_id = azurerm_storage_account.test.id + + target_region { + name = azurerm_resource_group.test.location + regional_replica_count = 1 + } +} +`, template, data.RandomInteger) +} + func (r SharedImageVersionResource) provisionSpecialized(data acceptance.TestData) string { template := ImageResource{}.setupManagedDisks(data) return fmt.Sprintf(` diff --git a/website/docs/r/shared_image_version.html.markdown b/website/docs/r/shared_image_version.html.markdown index bcb6a01364f3..93f02a24e376 100644 --- a/website/docs/r/shared_image_version.html.markdown +++ b/website/docs/r/shared_image_version.html.markdown @@ -57,6 +57,12 @@ The following arguments are supported: * `target_region` - (Required) One or more `target_region` blocks as documented below. +* `blob_uri` - (Optional) URI of the Azure Storage Blob used to create the Image Version. Changing this forces a new resource to be created. + +-> **NOTE:** You must specify exact one of `blob_uri`, `managed_image_id` and `os_disk_snapshot_id`. + +-> **NOTE:** `blob_uri` and `storage_account_id` must be specified together + * `end_of_life_date` - (Optional) The end of life date in RFC3339 format of the Image Version. * `exclude_from_latest` - (Optional) Should this Image Version be excluded from the `latest` filter? If set to `true` this Image Version won't be returned for the `latest` version. Defaults to `false`. @@ -65,12 +71,18 @@ The following arguments are supported: -> **NOTE:** The ID can be sourced from the `azurerm_image` [Data Source](https://www.terraform.io/docs/providers/azurerm/d/image.html) or [Resource](https://www.terraform.io/docs/providers/azurerm/r/image.html). +-> **NOTE:** You must specify exact one of `blob_uri`, `managed_image_id` and `os_disk_snapshot_id`. + * `os_disk_snapshot_id` - (Optional) The ID of the OS disk snapshot which should be used for this Shared Image Version. Changing this forces a new resource to be created. --> **NOTE:** You must specify exact one of `managed_image_id` and `os_disk_snapshot_id`. +-> **NOTE:** You must specify exact one of `blob_uri`, `managed_image_id` and `os_disk_snapshot_id`. * `replication_mode` - (Optional) Mode to be used for replication. Possible values are `Full` and `Shallow`. Defaults to `Full`. Changing this forces a new resource to be created. +* `storage_account_id` - (Optional) The ID of the Storage Account where the Blob exists. Changing this forces a new resource to be created. + +-> **NOTE:** `blob_uri` and `storage_account_id` must be specified together + * `tags` - (Optional) A collection of tags which should be applied to this resource. ---