diff --git a/azurerm/resource_arm_virtual_machine.go b/azurerm/resource_arm_virtual_machine.go index 06fb473925be..55d4ef4f179c 100644 --- a/azurerm/resource_arm_virtual_machine.go +++ b/azurerm/resource_arm_virtual_machine.go @@ -91,6 +91,7 @@ func resourceArmVirtualMachine() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{ string(compute.ResourceIdentityTypeSystemAssigned), string(compute.ResourceIdentityTypeUserAssigned), + string(compute.ResourceIdentityTypeSystemAssignedUserAssigned), }, false), }, "principal_id": { @@ -1264,7 +1265,7 @@ func expandAzureRmVirtualMachineIdentity(d *schema.ResourceData) *compute.Virtua Type: identityType, } - if vmIdentity.Type == compute.ResourceIdentityTypeUserAssigned { + if vmIdentity.Type == compute.ResourceIdentityTypeUserAssigned || vmIdentity.Type == compute.ResourceIdentityTypeSystemAssignedUserAssigned { vmIdentity.UserAssignedIdentities = identityIds } diff --git a/azurerm/resource_arm_virtual_machine_scale_set.go b/azurerm/resource_arm_virtual_machine_scale_set.go index b85b344d55bb..43f9bee11ff7 100644 --- a/azurerm/resource_arm_virtual_machine_scale_set.go +++ b/azurerm/resource_arm_virtual_machine_scale_set.go @@ -56,6 +56,7 @@ func resourceArmVirtualMachineScaleSet() *schema.Resource { ValidateFunc: validation.StringInSlice([]string{ string(compute.ResourceIdentityTypeSystemAssigned), string(compute.ResourceIdentityTypeUserAssigned), + string(compute.ResourceIdentityTypeSystemAssignedUserAssigned), }, false), }, "identity_ids": { @@ -1830,7 +1831,7 @@ func expandAzureRmVirtualMachineScaleSetIdentity(d *schema.ResourceData) *comput Type: identityType, } - if vmssIdentity.Type == compute.ResourceIdentityTypeUserAssigned { + if vmssIdentity.Type == compute.ResourceIdentityTypeUserAssigned || vmssIdentity.Type == compute.ResourceIdentityTypeSystemAssignedUserAssigned { vmssIdentity.UserAssignedIdentities = identityIds } diff --git a/azurerm/resource_arm_virtual_machine_scale_set_test.go b/azurerm/resource_arm_virtual_machine_scale_set_test.go index c0eef8f975f6..9548152d9243 100644 --- a/azurerm/resource_arm_virtual_machine_scale_set_test.go +++ b/azurerm/resource_arm_virtual_machine_scale_set_test.go @@ -661,6 +661,29 @@ func TestAccAzureRMVirtualMachineScaleSet_UserAssignedMSI(t *testing.T) { }) } +func TestAccAzureRMVirtualMachineScaleSet_multipleAssignedMSI(t *testing.T) { + resourceName := "azurerm_virtual_machine_scale_set.test" + ri := acctest.RandInt() + rs := acctest.RandString(14) + config := testAccAzureRMVirtualMachineScaleSetMultipleAssignedMSI(ri, testLocation(), rs) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMVirtualMachineScaleSetExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "identity.0.type", "SystemAssigned, UserAssigned"), + resource.TestCheckResourceAttr(resourceName, "identity.0.identity_ids.#", "1"), + resource.TestMatchResourceAttr(resourceName, "identity.0.principal_id", regexp.MustCompile(".+")), + ), + }, + }, + }) +} + func TestAccAzureRMVirtualMachineScaleSet_extension(t *testing.T) { resourceName := "azurerm_virtual_machine_scale_set.test" ri := acctest.RandInt() @@ -4819,3 +4842,98 @@ resource "azurerm_virtual_machine_scale_set" "test" { } `, rInt, location, mode, policy) } + +func testAccAzureRMVirtualMachineScaleSetMultipleAssignedMSI(rInt int, location string, rString string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%[1]d" + location = "%[2]s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%[1]d" + address_space = ["10.0.0.0/16"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctsub-%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_storage_account" "test" { + name = "accsa%[1]d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} + +resource "azurerm_user_assigned_identity" "test" { + name = "acctest%[3]s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" +} + +resource "azurerm_virtual_machine_scale_set" "test" { + name = "acctvmss-%[1]d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + upgrade_policy_mode = "Manual" + overprovision = false + + sku { + name = "Standard_D1_v2" + tier = "Standard" + capacity = 1 + } + + identity { + type = "SystemAssigned, UserAssigned" + identity_ids = ["${azurerm_user_assigned_identity.test.id}"] + } + + os_profile { + computer_name_prefix = "testvm-%[1]d" + admin_username = "myadmin" + admin_password = "Passwword1234" + } + + network_profile { + name = "TestNetworkProfile" + primary = true + + ip_configuration { + name = "TestIPConfiguration" + primary = true + subnet_id = "${azurerm_subnet.test.id}" + } + } + + storage_profile_os_disk { + name = "os-disk" + caching = "ReadWrite" + create_option = "FromImage" + vhd_containers = ["${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}"] + } + + storage_profile_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } +} + +`, rInt, location, rString) +} diff --git a/azurerm/resource_arm_virtual_machine_test.go b/azurerm/resource_arm_virtual_machine_test.go index d15eaf3f4f8e..0a669a5e39ce 100644 --- a/azurerm/resource_arm_virtual_machine_test.go +++ b/azurerm/resource_arm_virtual_machine_test.go @@ -80,6 +80,30 @@ func TestAccAzureRMVirtualMachine_UserAssignedIdentity(t *testing.T) { }) } +func TestAccAzureRMVirtualMachine_multipleAssignedIdentity(t *testing.T) { + var vm compute.VirtualMachine + resourceName := "azurerm_virtual_machine.test" + ri := acctest.RandInt() + rs := acctest.RandString(14) + config := testAccAzureRMVirtualMachineMultipleAssignedIdentity(ri, testLocation(), rs) + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMVirtualMachineScaleSetDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMVirtualMachineExists(resourceName, &vm), + resource.TestCheckResourceAttr(resourceName, "identity.0.type", "SystemAssigned, UserAssigned"), + resource.TestCheckResourceAttr(resourceName, "identity.0.identity_ids.#", "1"), + resource.TestMatchResourceAttr(resourceName, "identity.0.principal_id", regexp.MustCompile(".+")), + ), + }, + }, + }) +} + func testCheckAzureRMVirtualMachineExists(name string, vm *compute.VirtualMachine) resource.TestCheckFunc { return func(s *terraform.State) error { // Ensure we have enough information in state to look up in API @@ -421,3 +445,106 @@ resource "azurerm_virtual_machine" "test" { } `, rInt, location, rInt, rInt, rInt, rInt, rString, rInt, rInt) } + +func testAccAzureRMVirtualMachineMultipleAssignedIdentity(rInt int, location string, rString string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctvn-%d" + address_space = ["10.0.0.0/16"] + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctsub-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" +} + +resource "azurerm_network_interface" "test" { + name = "acctni-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + + ip_configuration { + name = "testconfiguration1" + subnet_id = "${azurerm_subnet.test.id}" + private_ip_address_allocation = "dynamic" + } +} + +resource "azurerm_storage_account" "test" { + name = "accsa%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags { + environment = "staging" + } +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} + +resource "azurerm_user_assigned_identity" "test" { + name = "acctest%s" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} + +resource "azurerm_virtual_machine" "test" { + name = "acctvm-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + network_interface_ids = ["${azurerm_network_interface.test.id}"] + vm_size = "Standard_D1_v2" + + storage_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04-LTS" + version = "latest" + } + + storage_os_disk { + name = "myosdisk1" + vhd_uri = "${azurerm_storage_account.test.primary_blob_endpoint}${azurerm_storage_container.test.name}/myosdisk1.vhd" + caching = "ReadWrite" + create_option = "FromImage" + disk_size_gb = "45" + } + + os_profile { + computer_name = "hn%d" + admin_username = "testadmin" + admin_password = "Password1234!" + } + + os_profile_linux_config { + disable_password_authentication = false + } + + identity { + type = "SystemAssigned, UserAssigned" + identity_ids = ["${azurerm_user_assigned_identity.test.id}"] + } + + tags { + environment = "Production" + cost-center = "Ops" + } +} +`, rInt, location, rInt, rInt, rInt, rInt, rString, rInt, rInt) +} diff --git a/website/docs/r/virtual_machine.html.markdown b/website/docs/r/virtual_machine.html.markdown index 1fa9fce3d172..4d5b3034b9af 100644 --- a/website/docs/r/virtual_machine.html.markdown +++ b/website/docs/r/virtual_machine.html.markdown @@ -174,7 +174,7 @@ A `boot_diagnostics` block supports the following: A `identity` block supports the following: -* `type` - (Required) The Managed Service Identity Type of this Virtual Machine. Possible values are `SystemAssigned` (where Azure will generate a Service Principal for you) and `UserAssigned` (where you can specify the Service Principal ID's) to be used by this Virtual Machine using the `identity_ids` field. +* `type` - (Required) The Managed Service Identity Type of this Virtual Machine. Possible values are `SystemAssigned` (where Azure will generate a Service Principal for you), `UserAssigned` (where you can specify the Service Principal ID's) to be used by this Virtual Machine using the `identity_ids` field, and `SystemAssigned, UserAssigned` which assigns both a system managed identity as well as the specified user assigned identities. -> **NOTE:** Managed Service Identity previously required the installation of a VM Extension, but this information [is now available via the Azure Instance Metadata Service](https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/overview#how-does-it-work). diff --git a/website/docs/r/virtual_machine_scale_set.html.markdown b/website/docs/r/virtual_machine_scale_set.html.markdown index 7c060433c26f..05ae722fa517 100644 --- a/website/docs/r/virtual_machine_scale_set.html.markdown +++ b/website/docs/r/virtual_machine_scale_set.html.markdown @@ -338,7 +338,7 @@ The following arguments are supported: `identity` supports the following: -* `type` - (Required) Specifies the identity type to be assigned to the scale set. Allowable values are `SystemAssigned` and `UserAssigned`. To enable Managed Service Identity (MSI) on all machines in the scale set, an extension with the type "ManagedIdentityExtensionForWindows" or "ManagedIdentityExtensionForLinux" must also be added. For the `SystemAssigned` identity the scale set's Service Principal ID (SPN) can be retrieved after the scale set has been created. See [documentation](https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/overview) for more information. +* `type` - (Required) Specifies the identity type to be assigned to the scale set. Allowable values are `SystemAssigned`, `UserAssigned`, and `SystemAssigned, UserAssigned`. For the `SystemAssigned` identity the scale set's Service Principal ID (SPN) can be retrieved after the scale set has been created. See [documentation](https://docs.microsoft.com/en-us/azure/active-directory/managed-service-identity/overview) for more information. * `identity_ids` - (Optional) Specifies a list of user managed identity ids to be assigned to the VMSS. Required if `type` is `UserAssigned`.