diff --git a/azurerm/internal/services/compute/linux_virtual_machine_resource.go b/azurerm/internal/services/compute/linux_virtual_machine_resource.go index d7bf227f5062..acb2ed83ebd0 100644 --- a/azurerm/internal/services/compute/linux_virtual_machine_resource.go +++ b/azurerm/internal/services/compute/linux_virtual_machine_resource.go @@ -206,10 +206,14 @@ func resourceLinuxVirtualMachine() *schema.Resource { "secret": linuxSecretSchema(), "source_image_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: computeValidate.ImageID, + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.Any( + computeValidate.ImageID, + computeValidate.SharedImageID, + computeValidate.SharedImageVersionID, + ), }, "source_image_reference": sourceImageReferenceSchema(true), diff --git a/azurerm/internal/services/compute/parse/shared_image.go b/azurerm/internal/services/compute/parse/shared_image.go new file mode 100644 index 000000000000..5158bcaf40f4 --- /dev/null +++ b/azurerm/internal/services/compute/parse/shared_image.go @@ -0,0 +1,38 @@ +package parse + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +type SharedImageId struct { + ResourceGroup string + Gallery string + Name string +} + +func SharedImageID(input string) (*SharedImageId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, fmt.Errorf("[ERROR] Unable to parse Image ID %q: %+v", input, err) + } + + set := SharedImageId{ + ResourceGroup: id.ResourceGroup, + } + + if set.Gallery, err = id.PopSegment("galleries"); err != nil { + return nil, err + } + + if set.Name, err = id.PopSegment("images"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &set, nil +} diff --git a/azurerm/internal/services/compute/parse/shared_image_test.go b/azurerm/internal/services/compute/parse/shared_image_test.go new file mode 100644 index 000000000000..5e3832642af7 --- /dev/null +++ b/azurerm/internal/services/compute/parse/shared_image_test.go @@ -0,0 +1,81 @@ +package parse + +import ( + "testing" +) + +func TestSharedImageID(t *testing.T) { + testData := []struct { + Name string + Input string + Error bool + Expect *SharedImageId + }{ + { + Name: "Empty", + Input: "", + Error: true, + }, + { + Name: "No Resource Groups Segment", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000", + Error: true, + }, + { + Name: "No Resource Groups Value", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/", + Error: true, + }, + { + Name: "Resource Group ID", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo/", + Error: true, + }, + { + Name: "Missing galleries segment", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.Compute/images/image1", + Error: true, + }, + { + Name: "Missing image Value", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.Compute/galleries/gallery1/images", + Error: true, + }, + { + Name: "Image ID", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Compute/galleries/gallery1/images/image1", + Error: false, + Expect: &SharedImageId{ + ResourceGroup: "mygroup1", + Gallery: "gallery1", + Name: "image1", + }, + }, + { + Name: "Wrong Casing", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Compute/galleries/gallery1/Images/image1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Name) + + actual, err := SharedImageID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expected a value but got an error: %s", err) + } + + if actual.Name != v.Expect.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expect.Name, actual.Name) + } + + if actual.ResourceGroup != v.Expect.ResourceGroup { + t.Fatalf("Expected %q but got %q for Resource Group", v.Expect.ResourceGroup, actual.ResourceGroup) + } + } +} diff --git a/azurerm/internal/services/compute/parse/shared_image_version.go b/azurerm/internal/services/compute/parse/shared_image_version.go new file mode 100644 index 000000000000..c11074e2d13d --- /dev/null +++ b/azurerm/internal/services/compute/parse/shared_image_version.go @@ -0,0 +1,43 @@ +package parse + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +type SharedImageVersionId struct { + ResourceGroup string + Version string + Gallery string + Name string +} + +func SharedImageVersionID(input string) (*SharedImageVersionId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, fmt.Errorf("[ERROR] Unable to parse Image ID %q: %+v", input, err) + } + + set := SharedImageVersionId{ + ResourceGroup: id.ResourceGroup, + } + + if set.Gallery, err = id.PopSegment("galleries"); err != nil { + return nil, err + } + + if set.Name, err = id.PopSegment("images"); err != nil { + return nil, err + } + + if set.Version, err = id.PopSegment("versions"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &set, nil +} diff --git a/azurerm/internal/services/compute/parse/shared_image_version_test.go b/azurerm/internal/services/compute/parse/shared_image_version_test.go new file mode 100644 index 000000000000..92c487034a7c --- /dev/null +++ b/azurerm/internal/services/compute/parse/shared_image_version_test.go @@ -0,0 +1,82 @@ +package parse + +import ( + "testing" +) + +func TestSharedImageVersionID(t *testing.T) { + testData := []struct { + Name string + Input string + Error bool + Expect *SharedImageVersionId + }{ + { + Name: "Empty", + Input: "", + Error: true, + }, + { + Name: "No Resource Groups Segment", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000", + Error: true, + }, + { + Name: "No Resource Groups Value", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/", + Error: true, + }, + { + Name: "Resource Group ID", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo/", + Error: true, + }, + { + Name: "Missing galleries segment", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.Compute/images/image1/versions/1.0.0", + Error: true, + }, + { + Name: "Missing image segment", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.Compute/galleries/gallery1/versions/1.0.0", + Error: true, + }, + { + Name: "Image ID", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Compute/galleries/gallery1/images/image1/versions/1.0.0", + Error: false, + Expect: &SharedImageVersionId{ + ResourceGroup: "mygroup1", + Gallery: "gallery1", + Name: "image1", + Version: "1.0.0", + }, + }, + { + Name: "Wrong Casing", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mygroup1/providers/Microsoft.Compute/galleries/gallery1/images/image1/Versions/1.0.0", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Name) + + actual, err := SharedImageVersionID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expected a value but got an error: %s", err) + } + + if actual.Name != v.Expect.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expect.Name, actual.Name) + } + + if actual.ResourceGroup != v.Expect.ResourceGroup { + t.Fatalf("Expected %q but got %q for Resource Group", v.Expect.ResourceGroup, actual.ResourceGroup) + } + } +} diff --git a/azurerm/internal/services/compute/tests/linux_virtual_machine_resource_images_test.go b/azurerm/internal/services/compute/tests/linux_virtual_machine_resource_images_test.go index 7062157baab6..cc763353f819 100644 --- a/azurerm/internal/services/compute/tests/linux_virtual_machine_resource_images_test.go +++ b/azurerm/internal/services/compute/tests/linux_virtual_machine_resource_images_test.go @@ -80,7 +80,7 @@ func TestAccLinuxVirtualMachine_imageFromSharedImageGallery(t *testing.T) { checkLinuxVirtualMachineExists(data.ResourceName), ), }, - data.ImportStep(), + data.ImportStep("admin_password"), }, }) } @@ -99,7 +99,7 @@ func TestAccLinuxVirtualMachine_imageFromSourceImageReference(t *testing.T) { checkLinuxVirtualMachineExists(data.ResourceName), ), }, - data.ImportStep(), + data.ImportStep("admin_password"), }, }) } @@ -108,9 +108,9 @@ func testLinuxVirtualMachine_imageFromExistingMachineDependencies(data acceptanc return fmt.Sprintf(` # note: whilst these aren't used in all tests, it saves us redefining these everywhere locals { - first_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+wWK73dCr+jgQOAxNsHAnNNNMEMWOHYEccp6wJm2gotpr9katuF/ZAdou5AaW1C61slRkHRkpRRX9FA9CYBiitZgvCCz+3nWNN7l/Up54Zps/pHWGZLHNJZRYyAB6j5yVLMVHIHriY49d/GZTZVNB8GoJv9Gakwc/fuEZYYl4YDFiGMBP///TzlI4jhiJzjKnEvqPFki5p2ZRJqcbCiF4pJrxUQR/RXqVFQdbRLZgYfJ8xGB878RENq3yQ39d8dVOkq4edbkzwcUmwwwkYVPIoDGsYLaRHnG+To7FvMeyO7xDVQkMKzopTQV8AuKpyvpqu0a9pWOMaiCyDytO7GGN you@me.com" + first_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC9ddAwoR0XBoT9kixLX6atgX9dovt9fpR1HO/R9jYwYnuB+SZ845KSqat+U0m6oagZhpsfcEEwjGGjQz6Z1rB6mvffsKq6i74cmm0jO564nBnZQeh31q3sFNs+XdrDtFmnYRqdPHhhr1sw0C/rxbiaE6nYZWRfHW//81nEePKMpjiN8JsrYQNbzEpz8QOBSquwBmXO+LVx//zAbY4jGTa4hjGeNzIgMJZ8Jk/11XbcxSK1PK43BrejHg6kctmEkYvMH/o12RfAeB8okGCRW3scwOozxVrHwxaPgEf03jig+Ag9V+GXNBabL5AWtxcuPN63rUfaAXEIXTHmndwVOxlpLrUf5ox1+ddGyWbLMXzd7akPioof5MNJMq/yuFGC5dY0Z6/+yGRNtShQesVo/czhKEPGIcsIi5gnKdfDB4i9ay2yz8ystnW6jbabcyqejk1Qc61wapaFdhUHL0iD/GW/5ZujDs5C3BT7EIgKLIfAaAx5TBEJyE1KQ/GEOifB8ztDl/gp99o+i2HKABtmYv12y4JVlEUkRckeLrw6luEb3ColHshsQcQGfudGFFgdEdcgBrV4Ch7IkLxVYQl3pegzZiirMPnRKh10r/Hrg6uYxn7sLeTJoD5VOKmqmeK4kFXsZMVtA6/SnxQtUKkKlfLBwBSDrrdgLjBV+KOndiwC7Q==" second_public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0/NDMj2wG6bSa6jbn6E3LYlUsYiWMp1CQ2sGAijPALW6OrSu30lz7nKpoh8Qdw7/A4nAJgweI5Oiiw5/BOaGENM70Go+VM8LQMSxJ4S7/8MIJEZQp5HcJZ7XDTcEwruknrd8mllEfGyFzPvJOx6QAQocFhXBW6+AlhM3gn/dvV5vdrO8ihjET2GoDUqXPYC57ZuY+/Fz6W3KV8V97BvNUhpY5yQrP5VpnyvvXNFQtzDfClTvZFPuoHQi3/KYPi6O0FSD74vo8JOBZZY09boInPejkm9fvHQqfh0bnN7B6XJoUwC1Qprrx+XIy7ust5AEn5XL7d4lOvcR14MxDDKEp you@me.com" - vm_name = "acctestvm-%s" + vm_name = "acctestsourcevm-%d" } resource "azurerm_resource_group" "test" { @@ -170,7 +170,7 @@ resource "azurerm_shared_image_gallery" "test" { resource_group_name = "${azurerm_resource_group.test.name}" location = "${azurerm_resource_group.test.location}" } -`, data.RandomString, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger) +`, data.RandomInteger, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger) } func testLinuxVirtualMachine_imageFromExistingMachinePrep(data acceptance.TestData) string { @@ -179,11 +179,14 @@ func testLinuxVirtualMachine_imageFromExistingMachinePrep(data acceptance.TestDa %s resource "azurerm_linux_virtual_machine" "source" { - name = "acctestsourceVM-%d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - size = "Standard_F2" - admin_username = "adminuser" + name = "acctestsourceVM-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + disable_password_authentication = false + admin_password = "Eung6ahthane2ied" + network_interface_ids = [ azurerm_network_interface.public.id, ] @@ -221,12 +224,15 @@ resource "azurerm_image" "test" { } resource "azurerm_linux_virtual_machine" "test" { - name = "acctestVM-%d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - size = "Standard_F2" - admin_username = "adminuser" - source_image_id = azurerm_image.test.id + name = "acctestVM-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + disable_password_authentication = false + admin_password = "Eung6ahthane2ied" + source_image_id = azurerm_image.test.id + network_interface_ids = [ azurerm_network_interface.test.id, ] @@ -241,6 +247,7 @@ resource "azurerm_linux_virtual_machine" "test" { storage_account_type = "Standard_LRS" } } + `, template, data.RandomInteger) } @@ -256,11 +263,14 @@ resource "azurerm_marketplace_agreement" "test" { } resource "azurerm_linux_virtual_machine" "test" { - name = "acctestVM-%d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - size = "Standard_F2" - admin_username = "adminuser" + name = "acctestVM-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + disable_password_authentication = false + admin_password = "Eung6ahthane2ied" + network_interface_ids = [ azurerm_network_interface.test.id, ] @@ -305,12 +315,6 @@ resource "azurerm_image" "test" { source_virtual_machine_id = azurerm_linux_virtual_machine.source.id } -resource "azurerm_shared_image_gallery" "test" { - name = "acctest-gallery-%d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location -} - resource "azurerm_shared_image" "test" { name = "acctest-gallery-image" gallery_name = azurerm_shared_image_gallery.test.name @@ -341,12 +345,15 @@ resource "azurerm_shared_image_version" "test" { } resource "azurerm_linux_virtual_machine" "test" { - name = "acctestVM-%d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - size = "Standard_F2" - admin_username = "adminuser" - source_image_id = azurerm_shared_image_version.test.id + name = "acctestVM-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + disable_password_authentication = false + admin_password = "Eung6ahthane2ied" + source_image_id = azurerm_shared_image_version.test.id + network_interface_ids = [ azurerm_network_interface.test.id, ] @@ -361,7 +368,7 @@ resource "azurerm_linux_virtual_machine" "test" { storage_account_type = "Standard_LRS" } } -`, template, data.RandomInteger, data.RandomInteger) +`, template, data.RandomInteger) } func testLinuxVirtualMachine_imageFromSourceImageReference(data acceptance.TestData) string { @@ -370,11 +377,14 @@ func testLinuxVirtualMachine_imageFromSourceImageReference(data acceptance.TestD %s resource "azurerm_linux_virtual_machine" "test" { - name = "acctestVM-%d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - size = "Standard_F2" - admin_username = "adminuser" + name = "acctestVM-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_F2" + admin_username = "adminuser" + disable_password_authentication = false + admin_password = "Eung6ahthane2ied" + network_interface_ids = [ azurerm_network_interface.test.id, ] diff --git a/azurerm/internal/services/compute/validate/shared_image.go b/azurerm/internal/services/compute/validate/shared_image.go new file mode 100644 index 000000000000..593b2265d639 --- /dev/null +++ b/azurerm/internal/services/compute/validate/shared_image.go @@ -0,0 +1,22 @@ +package validate + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/compute/parse" +) + +func SharedImageID(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return + } + + if _, err := parse.SharedImageID(v); err != nil { + errors = append(errors, fmt.Errorf("Can not parse %q as a resource id: %v", k, err)) + return + } + + return warnings, errors +} diff --git a/azurerm/internal/services/compute/validate/shared_image_version.go b/azurerm/internal/services/compute/validate/shared_image_version.go new file mode 100644 index 000000000000..22bd1beccca5 --- /dev/null +++ b/azurerm/internal/services/compute/validate/shared_image_version.go @@ -0,0 +1,22 @@ +package validate + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/compute/parse" +) + +func SharedImageVersionID(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return + } + + if _, err := parse.SharedImageVersionID(v); err != nil { + errors = append(errors, fmt.Errorf("Can not parse %q as a resource id: %v", k, err)) + return + } + + return warnings, errors +}