Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_linux_virtual_machine,azurerm_windows_virtual_machine - Support automatic_upgrade_enabled,disk_controller_type,os_image_notification,treat_failure_as_deployment_failure_enabled and vm_agent_platform_updates_enabled #23394

Merged
merged 10 commits into from
Feb 27, 2024
110 changes: 103 additions & 7 deletions internal/services/compute/linux_virtual_machine_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
azValidate "github.com/hashicorp/terraform-provider-azurerm/helpers/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/features"
"github.com/hashicorp/terraform-provider-azurerm/internal/locks"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/parse"
computeValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/validate"
Expand All @@ -39,7 +40,7 @@ import (
)

func resourceLinuxVirtualMachine() *pluginsdk.Resource {
return &pluginsdk.Resource{
resource := &pluginsdk.Resource{
Create: resourceLinuxVirtualMachineCreate,
Read: resourceLinuxVirtualMachineRead,
Update: resourceLinuxVirtualMachineUpdate,
Expand Down Expand Up @@ -340,6 +341,12 @@ func resourceLinuxVirtualMachine() *pluginsdk.Resource {
ValidateFunc: computeValidate.VirtualMachineScaleSetID,
},

"vm_agent_platform_updates_enabled": {
Type: pluginsdk.TypeBool,
Optional: true,
Default: false,
katbyte marked this conversation as resolved.
Show resolved Hide resolved
},

"vtpm_enabled": {
Type: pluginsdk.TypeBool,
Optional: true,
Expand All @@ -357,6 +364,8 @@ func resourceLinuxVirtualMachine() *pluginsdk.Resource {

"tags": tags.Schema(),

"os_image_notification": virtualMachineOsImageNotificationSchema(),

"termination_notification": virtualMachineTerminationNotificationSchema(),

"user_data": {
Expand Down Expand Up @@ -396,6 +405,20 @@ func resourceLinuxVirtualMachine() *pluginsdk.Resource {
},
},
}

if features.FourPointOhBeta() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason why this is limited to 4.0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @catriona-m, thank you for your review.
disk_controller_type is optional+computed, which may cause a diff after upgrading the provider for users in current provider version. I have added a comment to explain it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't appear to be optional and computed at the moment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MS-DOC clarifies that This property will be set to the default disk controller type if not specified.
For example, it will return SCSI as the default value if storageProfile.disk_controller_type is not set with the following configuration:

PUT /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/acctestRG-240131131145275307/providers/Microsoft.Compute/virtualMachines/acctestVM-240131131145275307?api-version=2023-03-01 HTTP/1.1
{
  "identity": {
    "type": "None"
  },
  "location": "westeurope",
  "properties": {
    "additionalCapabilities": {},
    "applicationProfile": {
      "galleryApplications": []
    },
    "diagnosticsProfile": {
      "bootDiagnostics": {
        "enabled": false,
        "storageUri": ""
      }
    },
    "extensionsTimeBudget": "PT1H30M",
    "hardwareProfile": {
      "vmSize": "Standard_E2bds_v5"
    },
    "networkProfile": {
      "networkInterfaces": [
        {
          "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/acctestRG-240131131145275307/providers/Microsoft.Network/networkInterfaces/acctestnic-240131131145275307",
          "properties": {
            "primary": true
          }
        }
      ]
    },
    "osProfile": {
      "computerName": "acctestVM-240131131145275307",
      "adminUsername": "adminuser",
      "linuxConfiguration": {
        "disablePasswordAuthentication": true,
        "ssh": {
          "publicKeys": [
            {
              "path": "/home/adminuser/.ssh/authorized_keys",
              "keyData": "ssh-rsa <key_data>"
            }
          ]
        },
        "provisionVMAgent": true,
        "patchSettings": {
          "patchMode": "ImageDefault",
          "assessmentMode": "ImageDefault"
        },
        "enableVMAgentPlatformUpdates": false
      },
      "secrets": [],
      "allowExtensionOperations": true
    },
    "priority": "Regular",
    "storageProfile": {
      "imageReference": {
        "offer": "0001-com-ubuntu-server-jammy",
        "publisher": "Canonical",
        "sku": "22_04-lts-gen2",
        "version": "latest"
      },
      "osDisk": {
        "osType": "Linux",
        "caching": "ReadWrite",
        "writeAcceleratorEnabled": false,
        "createOption": "FromImage",
        "managedDisk": {
          "storageAccountType": "Standard_LRS"
        }
      },
      "dataDisks": []
    }
  },
  "tags": {}
}

AzureRM Response for https:
{
  "name": "acctestVM-240131131145275307",
  "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/acctestRG-240131131145275307/providers/Microsoft.Compute/virtualMachines/acctestVM-240131131145275307",
  "type": "Microsoft.Compute/virtualMachines",
  "location": "westeurope",
  "tags": {},
  "properties": {
    "hardwareProfile": {
      "vmSize": "Standard_E2bds_v5"
    },
    "provisioningState": "Creating",
    "vmId": "218d5175-6dad-4df9-9d2f-7f689f133c12",
    "storageProfile": {
      "imageReference": {
        "publisher": "Canonical",
        "offer": "0001-com-ubuntu-server-jammy",
        "sku": "22_04-lts-gen2",
        "version": "latest",
        "exactVersion": "22.04.202401250"
      },
      "osDisk": {
        "osType": "Linux",
        "createOption": "FromImage",
        "caching": "ReadWrite",
        "writeAcceleratorEnabled": false,
        "managedDisk": {
          "storageAccountType": "Standard_LRS"
        },
        "deleteOption": "Detach",
        "diskSizeGB": 30
      },
      "dataDisks": [],
      "diskControllerType": "SCSI"
    },
    "osProfile": {
      "computerName": "acctestVM-240131131145275307",
      "adminUsername": "adminuser",
      "linuxConfiguration": {
        "disablePasswordAuthentication": true,
        "ssh": {
          "publicKeys": [
            {
              "path": "/home/adminuser/.ssh/authorized_keys",
              "keyData": "ssh-rsa <key_data>"
            }
          ]
        },
        "provisionVMAgent": true,
        "patchSettings": {
          "patchMode": "ImageDefault",
          "assessmentMode": "ImageDefault"
        },
        "enableVMAgentPlatformUpdates": false
      },
      "secrets": [],
      "allowExtensionOperations": true,
      "requireGuestProvisionSignal": true
    },
    "networkProfile": {
      "networkInterfaces": [
        {
          "id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/acctestRG-240131131145275307/providers/Microsoft.Network/networkInterfaces/acctestnic-240131131145275307",
          "properties": {
            "primary": true
          }
        }
      ]
    },
    "diagnosticsProfile": {
      "bootDiagnostics": {
        "enabled": false
      }
    },
    "priority": "Regular",
    "extensionsTimeBudget": "PT1H30M",
    "timeCreated": "2024-01-31T05:40:48.630956+00:00"
  }
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But the value is not optional computed here & if we know the default why can we not simply just configure that be default?

i still don't understand why this needs to wait till 4.0.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @katbyte, @tombuildsstuff,
If disk_controller_type is not set, its value in the Get-Response depends on the value of size, for example:

  • if size = Standard_E2bds_v5, disk_controller_type will be returned as SCSI
  • if size = Standard_E112ibds_v5, disk_controller_type will be returned as NVMe

Since disk_controller_type is not fixed for different size values, I cannot set a default value for it.

Copy link
Collaborator

@katbyte katbyte Feb 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could just make it computed thou? while we would rather have a proper default in this case as we can't computed should be fine? why would making it computed cause a diff for existing users? it would just adopt the value they have picked?

Copy link
Contributor Author

@ms-zhenhua ms-zhenhua Feb 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could just make it computed thou? while we would rather have a proper default in this case as we can't computed should be fine? why would making it computed cause a diff for existing users? it would just adopt the value they have picked?

I cannot just define disk_controller_type as a computed property because this property supports to be overwritten by users. May I define it as optional+computed w/o a default value, which can avoid the diff for existing users?
Though I know the best practice does not recommend using optional+computed anymore.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while generally yes that is correct and we shouldn't, there still are times where it has to be done because theres no "correct" way to dynamically specify a default. so in cases like this were the only way to get "the default" is from the API we have to use computed still.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code updated. Thanks.

// disk_controller_type is optional+computed, which may cause a diff after upgrading the provider. Make it take effect in next major version.
resource.Schema["disk_controller_type"] = &pluginsdk.Schema{
Type: pluginsdk.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{
string(compute.DiskControllerTypesNVMe),
string(compute.DiskControllerTypesSCSI),
}, false),
}
}

return resource
}

func resourceLinuxVirtualMachineCreate(d *pluginsdk.ResourceData, meta interface{}) error {
Expand Down Expand Up @@ -440,6 +463,7 @@ func resourceLinuxVirtualMachineCreate(d *pluginsdk.ResourceData, meta interface
computerName = id.Name
}
disablePasswordAuthentication := d.Get("disable_password_authentication").(bool)
vmAgentPlatformUpdatesEnabled := d.Get("vm_agent_platform_updates_enabled").(bool)
location := azure.NormalizeLocation(d.Get("location").(string))
identityRaw := d.Get("identity").([]interface{})
identity, err := expandVirtualMachineIdentity(identityRaw)
Expand Down Expand Up @@ -492,6 +516,7 @@ func resourceLinuxVirtualMachineCreate(d *pluginsdk.ResourceData, meta interface
AllowExtensionOperations: utils.Bool(allowExtensionOperations),
LinuxConfiguration: &compute.LinuxConfiguration{
DisablePasswordAuthentication: utils.Bool(disablePasswordAuthentication),
EnableVMAgentPlatformUpdates: utils.Bool(vmAgentPlatformUpdatesEnabled),
ProvisionVMAgent: utils.Bool(provisionVMAgent),
SSH: &compute.SSHConfiguration{
PublicKeys: &sshKeys,
Expand Down Expand Up @@ -520,6 +545,12 @@ func resourceLinuxVirtualMachineCreate(d *pluginsdk.ResourceData, meta interface
Tags: tags.Expand(t),
}

if features.FourPointOhBeta() {
if diskControllerType, ok := d.GetOk("disk_controller_type"); ok {
params.StorageProfile.DiskControllerType = compute.DiskControllerTypes(diskControllerType.(string))
}
}

if encryptionAtHostEnabled, ok := d.GetOk("encryption_at_host_enabled"); ok {
if encryptionAtHostEnabled.(bool) {
if compute.SecurityEncryptionTypesDiskWithVMGuestState == compute.SecurityEncryptionTypes(securityEncryptionType) {
Expand Down Expand Up @@ -634,8 +665,22 @@ func resourceLinuxVirtualMachineCreate(d *pluginsdk.ResourceData, meta interface
}
}

var osImageNotificationProfile *compute.OSImageNotificationProfile
var terminateNotificationProfile *compute.TerminateNotificationProfile

if v, ok := d.GetOk("os_image_notification"); ok {
osImageNotificationProfile = expandOsImageNotificationProfile(v.([]interface{}))
}

if v, ok := d.GetOk("termination_notification"); ok {
params.VirtualMachineProperties.ScheduledEventsProfile = expandVirtualMachineScheduledEventsProfile(v.([]interface{}))
terminateNotificationProfile = expandTerminateNotificationProfile(v.([]interface{}))
}

if terminateNotificationProfile != nil || osImageNotificationProfile != nil {
params.VirtualMachineProperties.ScheduledEventsProfile = &compute.ScheduledEventsProfile{
OsImageNotificationProfile: osImageNotificationProfile,
TerminateNotificationProfile: terminateNotificationProfile,
}
}

if !provisionVMAgent && allowExtensionOperations {
Expand Down Expand Up @@ -875,6 +920,7 @@ func resourceLinuxVirtualMachineRead(d *pluginsdk.ResourceData, meta interface{}
if config := profile.LinuxConfiguration; config != nil {
d.Set("disable_password_authentication", config.DisablePasswordAuthentication)
d.Set("provision_vm_agent", config.ProvisionVMAgent)
d.Set("vm_agent_platform_updates_enabled", config.EnableVMAgentPlatformUpdates)

flattenedSSHKeys, err := FlattenSSHKeys(config.SSH)
if err != nil {
Expand Down Expand Up @@ -923,6 +969,10 @@ func resourceLinuxVirtualMachineRead(d *pluginsdk.ResourceData, meta interface{}
d.Set("proximity_placement_group_id", proximityPlacementGroupId)

if profile := props.StorageProfile; profile != nil {
if features.FourPointOhBeta() {
d.Set("disk_controller_type", string(props.StorageProfile.DiskControllerType))
}

// the storage_account_type isn't returned so we need to look it up
flattenedOSDisk, err := flattenVirtualMachineOSDisk(ctx, disksClient, profile.OsDisk)
if err != nil {
Expand Down Expand Up @@ -951,7 +1001,12 @@ func resourceLinuxVirtualMachineRead(d *pluginsdk.ResourceData, meta interface{}
}

if scheduleProfile := props.ScheduledEventsProfile; scheduleProfile != nil {
if err := d.Set("termination_notification", flattenVirtualMachineScheduledEventsProfile(scheduleProfile)); err != nil {
_, isConfigured := d.GetOk("os_image_notification")
if err := d.Set("os_image_notification", flattenOsImageNotificationProfile(scheduleProfile.OsImageNotificationProfile, isConfigured)); err != nil {
return fmt.Errorf("setting `termination_notification`: %+v", err)
}

if err := d.Set("termination_notification", flattenTerminateNotificationProfile(scheduleProfile.TerminateNotificationProfile)); err != nil {
return fmt.Errorf("setting `termination_notification`: %+v", err)
}
}
Expand Down Expand Up @@ -1179,6 +1234,19 @@ func resourceLinuxVirtualMachineUpdate(d *pluginsdk.ResourceData, meta interface
}
}

if features.FourPointOhBeta() {
if d.HasChange("disk_controller_type") {
shouldUpdate = true
shouldDeallocate = true

if update.VirtualMachineProperties.StorageProfile == nil {
update.VirtualMachineProperties.StorageProfile = &compute.StorageProfile{}
}

update.VirtualMachineProperties.StorageProfile.DiskControllerType = compute.DiskControllerTypes(d.Get("disk_controller_type").(string))
}
}

if d.HasChange("os_disk") {
shouldUpdate = true

Expand All @@ -1192,9 +1260,11 @@ func resourceLinuxVirtualMachineUpdate(d *pluginsdk.ResourceData, meta interface
return fmt.Errorf("expanding `os_disk`: %+v", err)
}

update.VirtualMachineProperties.StorageProfile = &compute.StorageProfile{
OsDisk: osDisk,
if update.VirtualMachineProperties.StorageProfile == nil {
update.VirtualMachineProperties.StorageProfile = &compute.StorageProfile{}
}

update.VirtualMachineProperties.StorageProfile.OsDisk = osDisk
}

if d.HasChange("proximity_placement_group_id") {
Expand Down Expand Up @@ -1256,6 +1326,19 @@ func resourceLinuxVirtualMachineUpdate(d *pluginsdk.ResourceData, meta interface
}
}

if d.HasChange("vm_agent_platform_updates_enabled") {
shouldUpdate = true
if update.VirtualMachineProperties.OsProfile == nil {
update.VirtualMachineProperties.OsProfile = &compute.OSProfile{}
}

if update.VirtualMachineProperties.OsProfile.LinuxConfiguration == nil {
update.VirtualMachineProperties.OsProfile.LinuxConfiguration = &compute.LinuxConfiguration{}
}

update.VirtualMachineProperties.OsProfile.LinuxConfiguration.EnableVMAgentPlatformUpdates = utils.Bool(d.Get("vm_agent_platform_updates_enabled").(bool))
}

if d.HasChange("patch_mode") {
shouldUpdate = true
patchSettings := &compute.LinuxPatchSettings{}
Expand Down Expand Up @@ -1369,11 +1452,24 @@ func resourceLinuxVirtualMachineUpdate(d *pluginsdk.ResourceData, meta interface
update.OsProfile.AllowExtensionOperations = utils.Bool(allowExtensionOperations)
}

var osImageNotificationProfile *compute.OSImageNotificationProfile
var terminateNotificationProfile *compute.TerminateNotificationProfile

if d.HasChange("os_image_notification") {
shouldUpdate = true
osImageNotificationProfile = expandOsImageNotificationProfile(d.Get("os_image_notification").([]interface{}))
}

if d.HasChange("termination_notification") {
shouldUpdate = true
terminateNotificationProfile = expandTerminateNotificationProfile(d.Get("termination_notification").([]interface{}))
}

notificationRaw := d.Get("termination_notification").([]interface{})
update.ScheduledEventsProfile = expandVirtualMachineScheduledEventsProfile(notificationRaw)
if osImageNotificationProfile != nil || terminateNotificationProfile != nil {
update.ScheduledEventsProfile = &compute.ScheduledEventsProfile{
OsImageNotificationProfile: osImageNotificationProfile,
TerminateNotificationProfile: terminateNotificationProfile,
}
}

if d.HasChange("tags") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check"
"github.com/hashicorp/terraform-provider-azurerm/internal/features"
)

func TestAccLinuxVirtualMachine_diskOSBasic(t *testing.T) {
Expand Down Expand Up @@ -300,6 +301,39 @@ func TestAccLinuxVirtualMachine_diskOSStorageTypeUpdate(t *testing.T) {
})
}

func TestAccLinuxVirtualMachine_diskOSControllerType(t *testing.T) {
if !features.FourPointOhBeta() {
t.Skipf("Test applies after 4.0 only")
}

data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine", "test")
r := LinuxVirtualMachineResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.diskOSControllerTypeNVMe(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.diskOSControllerTypeSCSI(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.diskOSControllerTypeNVMe(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func TestAccLinuxVirtualMachine_diskOSWriteAcceleratorEnabled(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_linux_virtual_machine", "test")
r := LinuxVirtualMachineResource{}
Expand Down Expand Up @@ -892,6 +926,78 @@ func (r LinuxVirtualMachineResource) diskOSStorageAccountTypeWithRestrictedLocat
return r.diskOSStorageAccountType(data, accountType)
}

func (r LinuxVirtualMachineResource) diskOSControllerTypeSCSI(data acceptance.TestData) string {
return fmt.Sprintf(`
%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_B1s"
admin_username = "adminuser"
disk_controller_type = "SCSI"

network_interface_ids = [
azurerm_network_interface.test.id,
]

admin_ssh_key {
username = "adminuser"
public_key = local.first_public_key
}

os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}

source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts-gen2"
version = "latest"
}
}
`, r.template(data), data.RandomInteger)
}

func (r LinuxVirtualMachineResource) diskOSControllerTypeNVMe(data acceptance.TestData) string {
return fmt.Sprintf(`
%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_E2bds_v5"
admin_username = "adminuser"
disk_controller_type = "NVMe"

network_interface_ids = [
azurerm_network_interface.test.id,
]

admin_ssh_key {
username = "adminuser"
public_key = local.first_public_key
}

os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}

source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts-gen2"
version = "latest"
}
}
`, r.template(data), data.RandomInteger)
}

func (r LinuxVirtualMachineResource) diskOSWriteAcceleratorEnabled(data acceptance.TestData, enabled bool) string {
return fmt.Sprintf(`
%s
Expand Down
Loading
Loading