From 8466529542c53af08f8c7ecbd162a5cf97990633 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Fri, 11 Oct 2024 09:11:37 +0000 Subject: [PATCH 01/16] wip --- .../parse/stack_hci_virtual_machine.go | 134 ++++ .../parse/stack_hci_virtual_machine_test.go | 267 +++++++ .../services/azurestackhci/resourceids.go | 3 + .../stack_hci_virtual_machine_resource.go | 681 ++++++++++++++++++ ...stack_hci_virtual_machine_resource_test.go | 1 + .../validate/stack_hci_virtual_machine_id.go | 26 + .../stack_hci_virtual_machine_id_test.go | 91 +++ 7 files changed, 1203 insertions(+) create mode 100644 internal/services/azurestackhci/parse/stack_hci_virtual_machine.go create mode 100644 internal/services/azurestackhci/parse/stack_hci_virtual_machine_test.go create mode 100644 internal/services/azurestackhci/resourceids.go create mode 100644 internal/services/azurestackhci/stack_hci_virtual_machine_resource.go create mode 100644 internal/services/azurestackhci/stack_hci_virtual_machine_resource_test.go create mode 100644 internal/services/azurestackhci/validate/stack_hci_virtual_machine_id.go create mode 100644 internal/services/azurestackhci/validate/stack_hci_virtual_machine_id_test.go diff --git a/internal/services/azurestackhci/parse/stack_hci_virtual_machine.go b/internal/services/azurestackhci/parse/stack_hci_virtual_machine.go new file mode 100644 index 000000000000..a7b9bf0851b6 --- /dev/null +++ b/internal/services/azurestackhci/parse/stack_hci_virtual_machine.go @@ -0,0 +1,134 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type StackHCIVirtualMachineId struct { + SubscriptionId string + ResourceGroup string + MachineName string + VirtualMachineInstanceName string +} + +func NewStackHCIVirtualMachineID(subscriptionId, resourceGroup, machineName, virtualMachineInstanceName string) StackHCIVirtualMachineId { + return StackHCIVirtualMachineId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + MachineName: machineName, + VirtualMachineInstanceName: virtualMachineInstanceName, + } +} + +func (id StackHCIVirtualMachineId) String() string { + segments := []string{ + fmt.Sprintf("Virtual Machine Instance Name %q", id.VirtualMachineInstanceName), + fmt.Sprintf("Machine Name %q", id.MachineName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Stack H C I Virtual Machine", segmentsStr) +} + +func (id StackHCIVirtualMachineId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.HybridCompute/machines/%s/providers/Microsoft.AzureStackHCI/virtualMachineInstances/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.MachineName, id.VirtualMachineInstanceName) +} + +// StackHCIVirtualMachineID parses a StackHCIVirtualMachine ID into an StackHCIVirtualMachineId struct +func StackHCIVirtualMachineID(input string) (*StackHCIVirtualMachineId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, fmt.Errorf("parsing %q as an StackHCIVirtualMachine ID: %+v", input, err) + } + + resourceId := StackHCIVirtualMachineId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.MachineName, err = id.PopSegment("machines"); err != nil { + return nil, err + } + if resourceId.VirtualMachineInstanceName, err = id.PopSegment("virtualMachineInstances"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} + +// StackHCIVirtualMachineIDInsensitively parses an StackHCIVirtualMachine ID into an StackHCIVirtualMachineId struct, insensitively +// This should only be used to parse an ID for rewriting, the StackHCIVirtualMachineID +// method should be used instead for validation etc. +// +// Whilst this may seem strange, this enables Terraform have consistent casing +// which works around issues in Core, whilst handling broken API responses. +func StackHCIVirtualMachineIDInsensitively(input string) (*StackHCIVirtualMachineId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := StackHCIVirtualMachineId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + // find the correct casing for the 'machines' segment + machinesKey := "machines" + for key := range id.Path { + if strings.EqualFold(key, machinesKey) { + machinesKey = key + break + } + } + if resourceId.MachineName, err = id.PopSegment(machinesKey); err != nil { + return nil, err + } + + // find the correct casing for the 'virtualMachineInstances' segment + virtualMachineInstancesKey := "virtualMachineInstances" + for key := range id.Path { + if strings.EqualFold(key, virtualMachineInstancesKey) { + virtualMachineInstancesKey = key + break + } + } + if resourceId.VirtualMachineInstanceName, err = id.PopSegment(virtualMachineInstancesKey); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/azurestackhci/parse/stack_hci_virtual_machine_test.go b/internal/services/azurestackhci/parse/stack_hci_virtual_machine_test.go new file mode 100644 index 000000000000..16befe469efa --- /dev/null +++ b/internal/services/azurestackhci/parse/stack_hci_virtual_machine_test.go @@ -0,0 +1,267 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = StackHCIVirtualMachineId{} + +func TestStackHCIVirtualMachineIDFormatter(t *testing.T) { + actual := NewStackHCIVirtualMachineID("00000000-0000-0000-0000-000000000000", "resourceGroup1", "machine1", "default").ID() + expected := "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.AzureStackHCI/virtualMachineInstances/default" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestStackHCIVirtualMachineID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *StackHCIVirtualMachineId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/", + Error: true, + }, + + { + // missing MachineName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/", + Error: true, + }, + + { + // missing value for MachineName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/machines/", + Error: true, + }, + + { + // missing VirtualMachineInstanceName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.AzureStackHCI/", + Error: true, + }, + + { + // missing value for VirtualMachineInstanceName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.AzureStackHCI/virtualMachineInstances/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.AzureStackHCI/virtualMachineInstances/default", + Expected: &StackHCIVirtualMachineId{ + SubscriptionId: "00000000-0000-0000-0000-000000000000", + ResourceGroup: "resourceGroup1", + MachineName: "machine1", + VirtualMachineInstanceName: "default", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/00000000-0000-0000-0000-000000000000/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.HYBRIDCOMPUTE/MACHINES/MACHINE1/PROVIDERS/MICROSOFT.AZURESTACKHCI/VIRTUALMACHINEINSTANCES/DEFAULT", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := StackHCIVirtualMachineID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.MachineName != v.Expected.MachineName { + t.Fatalf("Expected %q but got %q for MachineName", v.Expected.MachineName, actual.MachineName) + } + if actual.VirtualMachineInstanceName != v.Expected.VirtualMachineInstanceName { + t.Fatalf("Expected %q but got %q for VirtualMachineInstanceName", v.Expected.VirtualMachineInstanceName, actual.VirtualMachineInstanceName) + } + } +} + +func TestStackHCIVirtualMachineIDInsensitively(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *StackHCIVirtualMachineId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/", + Error: true, + }, + + { + // missing MachineName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/", + Error: true, + }, + + { + // missing value for MachineName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/machines/", + Error: true, + }, + + { + // missing VirtualMachineInstanceName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.AzureStackHCI/", + Error: true, + }, + + { + // missing value for VirtualMachineInstanceName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.AzureStackHCI/virtualMachineInstances/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.AzureStackHCI/virtualMachineInstances/default", + Expected: &StackHCIVirtualMachineId{ + SubscriptionId: "00000000-0000-0000-0000-000000000000", + ResourceGroup: "resourceGroup1", + MachineName: "machine1", + VirtualMachineInstanceName: "default", + }, + }, + + { + // lower-cased segment names + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.AzureStackHCI/virtualmachineinstances/default", + Expected: &StackHCIVirtualMachineId{ + SubscriptionId: "00000000-0000-0000-0000-000000000000", + ResourceGroup: "resourceGroup1", + MachineName: "machine1", + VirtualMachineInstanceName: "default", + }, + }, + + { + // upper-cased segment names + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/MACHINES/machine1/providers/Microsoft.AzureStackHCI/VIRTUALMACHINEINSTANCES/default", + Expected: &StackHCIVirtualMachineId{ + SubscriptionId: "00000000-0000-0000-0000-000000000000", + ResourceGroup: "resourceGroup1", + MachineName: "machine1", + VirtualMachineInstanceName: "default", + }, + }, + + { + // mixed-cased segment names + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/MaChInEs/machine1/providers/Microsoft.AzureStackHCI/ViRtUaLmAcHiNeInStAnCeS/default", + Expected: &StackHCIVirtualMachineId{ + SubscriptionId: "00000000-0000-0000-0000-000000000000", + ResourceGroup: "resourceGroup1", + MachineName: "machine1", + VirtualMachineInstanceName: "default", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := StackHCIVirtualMachineIDInsensitively(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.MachineName != v.Expected.MachineName { + t.Fatalf("Expected %q but got %q for MachineName", v.Expected.MachineName, actual.MachineName) + } + if actual.VirtualMachineInstanceName != v.Expected.VirtualMachineInstanceName { + t.Fatalf("Expected %q but got %q for VirtualMachineInstanceName", v.Expected.VirtualMachineInstanceName, actual.VirtualMachineInstanceName) + } + } +} diff --git a/internal/services/azurestackhci/resourceids.go b/internal/services/azurestackhci/resourceids.go new file mode 100644 index 000000000000..dcb088cbc67b --- /dev/null +++ b/internal/services/azurestackhci/resourceids.go @@ -0,0 +1,3 @@ +package azurestackhci + +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=StackHCIVirtualMachine -id=/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.AzureStackHCI/virtualMachineInstances/default -rewrite=true diff --git a/internal/services/azurestackhci/stack_hci_virtual_machine_resource.go b/internal/services/azurestackhci/stack_hci_virtual_machine_resource.go new file mode 100644 index 000000000000..794008f5248a --- /dev/null +++ b/internal/services/azurestackhci/stack_hci_virtual_machine_resource.go @@ -0,0 +1,681 @@ +package azurestackhci + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" + "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/marketplacegalleryimages" + "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/networkinterfaces" + "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/storagecontainers" + "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/virtualharddisks" + "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/virtualmachineinstances" + "github.com/hashicorp/go-azure-sdk/resource-manager/extendedlocation/2021-08-15/customlocations" + "github.com/hashicorp/go-azure-sdk/resource-manager/hybridcompute/2024-07-10/machines" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/azurestackhci/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/azurestackhci/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" +) + +var ( + _ sdk.Resource = StackHCIVirtualMachineResource{} + _ sdk.ResourceWithUpdate = StackHCIVirtualMachineResource{} +) + +type StackHCIVirtualMachineResource struct{} + +func (StackHCIVirtualMachineResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return validate.StackHCIVirtualMachineID +} + +func (StackHCIVirtualMachineResource) ResourceType() string { + return "azurerm_stack_hci_virtual_machine" +} + +func (StackHCIVirtualMachineResource) ModelObject() interface{} { + return &StackHCIVirtualMachineResourceModel{} +} + +type StackHCIVirtualMachineResourceModel struct { + ArcMachineId string `tfschema:"arc_machine_id"` + CustomLocationId string `tfschema:"custom_location_id"` + HardwareProfile []StackHCIVirtualMachineHardwareProfile `tfschema:"hardware_profile"` + HttpProxyConfiguration []StackHCIVirtualMachineHttpProxyConfiguration `tfschema:"http_proxy_configuration"` + Identity []identity.ModelSystemAssigned `tfschema:"identity"` + NetworkProfile []StackHCIVirtualMachineNetworkProfile `tfschema:"network_profile"` + OsProfile []StackHCIVirtualMachineOsProfile `tfschema:"os_profile"` + SecurityProfile []StackHCIVirtualMachineSecurityProfile `tfschema:"security_profile"` + StorageProfile []StackHCIVirtualMachineStorageProfile `tfschema:"storage_profile"` +} + +type StackHCIVirtualMachineHardwareProfile struct { + DynamicMemory []StackHCIVirtualMachineDynamicMemory `tfschema:"dynamic_memory"` + MemoryMb int64 `tfschema:"memory_mb"` + ProcessorNumber int64 `tfschema:"processor_number"` + VmSize string `tfschema:"vm_size"` +} + +type StackHCIVirtualMachineDynamicMemory struct { + MaximumMemoryMb int64 `tfschema:"maximum_memory_mb"` + MinimumMemoryMb int64 `tfschema:"minimum_memory_mb"` + TargetMemoryBuffer int64 `tfschema:"target_memory_buffer"` +} + +type StackHCIVirtualMachineNetworkProfile struct { + NetworkInterfaceIds []string `tfschema:"network_interface_ids"` +} + +type StackHCIVirtualMachineOsProfile struct { + AdminUsername string `tfschema:"admin_username"` + AdminPassword string `tfschema:"admin_password"` + ComputerName string `tfschema:"computer_name"` + LinuxConfiguration []StackHCIVirtualMachineLinuxConfiguration `tfschema:"linux_configuration"` + WindowsConfiguration []StackHCIVirtualMachineWindowsConfiguration `tfschema:"windows_configuration"` +} + +type StackHCIVirtualMachineLinuxConfiguration struct { + PasswordAuthenticationEnabled bool `tfschema:"password_authentication_enabled"` + ProvisionVmAgentEnabled bool `tfschema:"provision_vm_agent_enabled"` + ProvisionVmConfigAgentEnabled bool `tfschema:"provision_vm_config_agent_enabled"` + SshPublicKey []StackHCIVirtualMachineSshPublicKey `tfschema:"ssh_public_key"` +} + +type StackHCIVirtualMachineWindowsConfiguration struct { + AutomaticUpdateEnabled bool `tfschema:"automatic_update_enabled"` + ProvisionVmAgentEnabled bool `tfschema:"provision_vm_agent_enabled"` + ProvisionVmConfigAgentEnabled bool `tfschema:"provision_vm_config_agent_enabled"` + SshPublicKey []StackHCIVirtualMachineSshPublicKey `tfschema:"ssh_public_key"` + TimeZone string `tfschema:"time_zone"` +} + +type StackHCIVirtualMachineSshPublicKey struct { + KeyData string `tfschema:"key_data"` + Path string `tfschema:"path"` +} + +type StackHCIVirtualMachineSecurityProfile struct { + SecureBootEnabled bool `tfschema:"secure_boot_enabled"` + SecurityType string `tfschema:"security_type"` + TpmEnabled bool `tfschema:"tpm_enabled"` +} + +type StackHCIVirtualMachineStorageProfile struct { + DataDiskIds []string `tfschema:"data_disk_ids"` + ImageId string `tfschema:"image_id"` + OsDisk []StackHCIVirtualMachineOsDisk `tfschema:"os_disk"` + VmConfigStoragePathId string `tfschema:"vm_config_storage_path_id"` +} + +type StackHCIVirtualMachineOsDisk struct { + DiskId string `tfschema:"disk_id"` + OsType string `tfschema:"os_type"` +} + +type StackHCIVirtualMachineHttpProxyConfiguration struct { + HttpProxy string `tfschema:"http_proxy"` + HttpsProxy string `tfschema:"https_proxy"` + NoProxy []string `tfschema:"no_proxy"` + TrustedCa string `tfschema:"trusted_ca"` +} + +func (StackHCIVirtualMachineResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "arc_machine_id": commonschema.ResourceIDReferenceRequiredForceNew(&machines.MachineId{}), + + "custom_location_id": commonschema.ResourceIDReferenceRequiredForceNew(&customlocations.CustomLocationId{}), + + "hardware_profile": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "vm_size": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(virtualmachineinstances.PossibleValuesForVMSizeEnum(), false), + }, + + "processor_number": { + Type: pluginsdk.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntAtLeast(1), + }, + + "memory_mb": { + Type: pluginsdk.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntAtLeast(1), + }, + + "dynamic_memory": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "maximum_memory_mb": { + Type: pluginsdk.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntAtLeast(1), + }, + + "minimum_memory_mb": { + Type: pluginsdk.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntAtLeast(1), + }, + + "target_memory_buffer": { + Type: pluginsdk.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntAtLeast(1), + }, + }, + }, + }, + }, + }, + }, + + "network_profile": { + Type: pluginsdk.TypeList, + Required: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "network_interface_ids": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: networkinterfaces.ValidateNetworkInterfaceID, + }, + }, + }, + }, + }, + + "os_profile": { + Type: pluginsdk.TypeList, + Required: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "admin_username": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "admin_password": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + Sensitive: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "computer_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "linux_configuration": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "password_authentication_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, + + "ssh_public_key": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "path": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "key_data": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "provision_vm_agent_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + + "provision_vm_config_agent_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + }, + }, + }, + + "windows_configuration": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "automatic_update_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, + + "ssh_public_key": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "path": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "key_data": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "time_zone": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "provision_vm_agent_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + + "provision_vm_config_agent_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + }, + }, + }, + }, + }, + }, + + "security_profile": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "tpm_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, + + "secure_boot_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, + + "security_type": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(virtualmachineinstances.PossibleValuesForSecurityTypes(), false), + }, + }, + }, + }, + + "storage_profile": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "data_disk_ids": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: virtualharddisks.ValidateVirtualHardDiskID, + }, + }, + + "image_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: marketplacegalleryimages.ValidateMarketplaceGalleryImageID, + }, + + "os_disk": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "disk_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: virtualharddisks.ValidateVirtualHardDiskID, + }, + + "os_type": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(virtualmachineinstances.PossibleValuesForOperatingSystemTypes(), false), + }, + }, + }, + }, + + "vm_config_storage_path_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: storagecontainers.ValidateStorageContainerID, + }, + }, + }, + }, + + "http_proxy_configuration": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{}, + }, + }, + + "identity": commonschema.SystemAssignedIdentityOptional(), + } +} + +func (StackHCIVirtualMachineResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (r StackHCIVirtualMachineResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.AzureStackHCI.VirtualMachineInstances + + var config StackHCIVirtualMachineResourceModel + if err := metadata.Decode(&config); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + arcMachineId, err := machines.ParseMachineID(config.ArcMachineId) + if err != nil { + return fmt.Errorf("parsing `arc_machine_id`: %+v", err) + } + + id := parse.NewStackHCIVirtualMachineID(arcMachineId.SubscriptionId, arcMachineId.ResourceGroupName, arcMachineId.MachineName, "default") + scopeId := commonids.NewScopeID(config.ArcMachineId) + + existing, err := client.Get(ctx, scopeId) + if err != nil && !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + } + if !response.WasNotFound(existing.HttpResponse) { + return metadata.ResourceRequiresImport(r.ResourceType(), id) + } + + payload := virtualmachineinstances.VirtualMachineInstance{ + ExtendedLocation: &virtualmachineinstances.ExtendedLocation{ + Name: pointer.To(config.CustomLocationId), + Type: pointer.To(virtualmachineinstances.ExtendedLocationTypesCustomLocation), + }, + Properties: &virtualmachineinstances.VirtualMachineInstanceProperties{ + HardwareProfile: expandVirtualMachineInstanceHardwareProfile(config.HardwareProfile), + HTTPProxyConfig: nil, + NetworkProfile: nil, + OsProfile: nil, + SecurityProfile: nil, + StorageProfile: nil, + }, + } + + if err := client.CreateOrUpdateThenPoll(ctx, scopeId, payload); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + + return nil + }, + } +} + +func (r StackHCIVirtualMachineResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.AzureStackHCI.VirtualMachineInstances + + id, err := parse.StackHCIVirtualMachineID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + arcMachineId := machines.NewMachineID(id.SubscriptionId, id.ResourceGroup, id.MachineName) + scopeId := commonids.NewScopeID(arcMachineId.String()) + + resp, err := client.Get(ctx, scopeId) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(id) + } + + return fmt.Errorf("retrieving %s: %+v", id, err) + } + + schema := StackHCIVirtualMachineResourceModel{ + ArcMachineId: arcMachineId.String(), + } + + if model := resp.Model; model != nil { + schema.Identity = identity.FlattenSystemAssignedToModel(model.Identity) + + if model.ExtendedLocation != nil && model.ExtendedLocation.Name != nil { + customLocationId, err := customlocations.ParseCustomLocationIDInsensitively(*model.ExtendedLocation.Name) + if err != nil { + return err + } + + schema.CustomLocationId = customLocationId.ID() + } + + if props := model.Properties; props != nil { + schema.HardwareProfile = flattenVirtualMachineInstanceHardwareProfile(props.HardwareProfile) + schema.HttpProxyConfiguration = nil + schema.NetworkProfile = nil + schema.OsProfile = nil + schema.SecurityProfile = nil + schema.StorageProfile = nil + } + } + + return metadata.Encode(&schema) + }, + } +} + +func (r StackHCIVirtualMachineResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.AzureStackHCI.VirtualMachineInstances + + id, err := parse.StackHCIVirtualMachineID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + arcMachineId := machines.NewMachineID(id.SubscriptionId, id.ResourceGroup, id.MachineName) + scopeId := commonids.NewScopeID(arcMachineId.String()) + + var model StackHCIVirtualMachineResourceModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + parameters := virtualmachineinstances.VirtualMachineInstanceUpdateRequest{} + + if metadata.ResourceData.HasChange("") { + } + + if err := client.UpdateThenPoll(ctx, scopeId, parameters); err != nil { + return fmt.Errorf("updating %s: %+v", id, err) + } + return nil + }, + } +} + +func (r StackHCIVirtualMachineResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.AzureStackHCI.VirtualMachineInstances + + id, err := parse.StackHCIVirtualMachineID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + arcMachineId := machines.NewMachineID(id.SubscriptionId, id.ResourceGroup, id.MachineName) + scopeId := commonids.NewScopeID(arcMachineId.String()) + + if err := client.DeleteThenPoll(ctx, scopeId); err != nil { + return fmt.Errorf("deleting %s: %+v", id, err) + } + + return nil + }, + } +} + +func expandVirtualMachineInstanceHardwareProfile(input []StackHCIVirtualMachineHardwareProfile) *virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfile { + if len(input) == 0 { + return nil + } + + v := input[0] + output := &virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfile{ + DynamicMemoryConfig: expandVirtualMachineInstanceDynamicMemory(v.DynamicMemory), + MemoryMB: pointer.To(v.MemoryMb), + Processors: pointer.To(v.ProcessorNumber), + VMSize: pointer.To(virtualmachineinstances.VMSizeEnum(v.VmSize)), + } + + return output +} + +func flattenVirtualMachineInstanceHardwareProfile(input *virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfile) []StackHCIVirtualMachineHardwareProfile { + if input == nil { + return make([]StackHCIVirtualMachineHardwareProfile, 0) + } + + return []StackHCIVirtualMachineHardwareProfile{ + { + DynamicMemory: nil, + MemoryMb: pointer.From(input.MemoryMB), + ProcessorNumber: pointer.From(input.Processors), + VmSize: string(pointer.From(input.VMSize)), + }, + } +} + +func expandVirtualMachineInstanceDynamicMemory(input []StackHCIVirtualMachineDynamicMemory) *virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfileDynamicMemoryConfig { + if len(input) == 0 { + return nil + } + + v := input[0] + output := &virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfileDynamicMemoryConfig{ + MaximumMemoryMB: pointer.To(v.MaximumMemoryMb), + MinimumMemoryMB: pointer.To(v.MinimumMemoryMb), + } + + return output +} + +func flattenVirtualMachineInstanceDynamicMemory(input *virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfileDynamicMemoryConfig) []StackHCIVirtualMachineDynamicMemory { + if input == nil { + return make([]StackHCIVirtualMachineDynamicMemory, 0) + } + + return []StackHCIVirtualMachineDynamicMemory{ + { + MaximumMemoryMb: pointer.From(input.MaximumMemoryMB), + MinimumMemoryMb: pointer.From(input.MinimumMemoryMB), + }, + } +} diff --git a/internal/services/azurestackhci/stack_hci_virtual_machine_resource_test.go b/internal/services/azurestackhci/stack_hci_virtual_machine_resource_test.go new file mode 100644 index 000000000000..95c7bd9ea399 --- /dev/null +++ b/internal/services/azurestackhci/stack_hci_virtual_machine_resource_test.go @@ -0,0 +1 @@ +package azurestackhci_test diff --git a/internal/services/azurestackhci/validate/stack_hci_virtual_machine_id.go b/internal/services/azurestackhci/validate/stack_hci_virtual_machine_id.go new file mode 100644 index 000000000000..7b7e55babd0b --- /dev/null +++ b/internal/services/azurestackhci/validate/stack_hci_virtual_machine_id.go @@ -0,0 +1,26 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/azurestackhci/parse" +) + +func StackHCIVirtualMachineID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.StackHCIVirtualMachineID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/azurestackhci/validate/stack_hci_virtual_machine_id_test.go b/internal/services/azurestackhci/validate/stack_hci_virtual_machine_id_test.go new file mode 100644 index 000000000000..b05f62f59b68 --- /dev/null +++ b/internal/services/azurestackhci/validate/stack_hci_virtual_machine_id_test.go @@ -0,0 +1,91 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestStackHCIVirtualMachineID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/", + Valid: false, + }, + + { + // missing MachineName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/", + Valid: false, + }, + + { + // missing value for MachineName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/machines/", + Valid: false, + }, + + { + // missing VirtualMachineInstanceName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.AzureStackHCI/", + Valid: false, + }, + + { + // missing value for VirtualMachineInstanceName + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.AzureStackHCI/virtualMachineInstances/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resourceGroup1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.AzureStackHCI/virtualMachineInstances/default", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/00000000-0000-0000-0000-000000000000/RESOURCEGROUPS/RESOURCEGROUP1/PROVIDERS/MICROSOFT.HYBRIDCOMPUTE/MACHINES/MACHINE1/PROVIDERS/MICROSOFT.AZURESTACKHCI/VIRTUALMACHINEINSTANCES/DEFAULT", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := StackHCIVirtualMachineID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} From fd6fdc6a45a2f6ffc3152859bfe56ce86b0b8f9a Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Thu, 24 Oct 2024 07:13:48 +0000 Subject: [PATCH 02/16] wip --- .../stack_hci_virtual_machine_resource.go | 546 +++++++++++++++++- 1 file changed, 518 insertions(+), 28 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_virtual_machine_resource.go b/internal/services/azurestackhci/stack_hci_virtual_machine_resource.go index 794008f5248a..acd313ce6685 100644 --- a/internal/services/azurestackhci/stack_hci_virtual_machine_resource.go +++ b/internal/services/azurestackhci/stack_hci_virtual_machine_resource.go @@ -68,6 +68,13 @@ type StackHCIVirtualMachineDynamicMemory struct { TargetMemoryBuffer int64 `tfschema:"target_memory_buffer"` } +type StackHCIVirtualMachineHttpProxyConfiguration struct { + HttpProxy string `tfschema:"http_proxy"` + HttpsProxy string `tfschema:"https_proxy"` + NoProxy []string `tfschema:"no_proxy"` + TrustedCa string `tfschema:"trusted_ca"` +} + type StackHCIVirtualMachineNetworkProfile struct { NetworkInterfaceIds []string `tfschema:"network_interface_ids"` } @@ -118,13 +125,6 @@ type StackHCIVirtualMachineOsDisk struct { OsType string `tfschema:"os_type"` } -type StackHCIVirtualMachineHttpProxyConfiguration struct { - HttpProxy string `tfschema:"http_proxy"` - HttpsProxy string `tfschema:"https_proxy"` - NoProxy []string `tfschema:"no_proxy"` - TrustedCa string `tfschema:"trusted_ca"` -} - func (StackHCIVirtualMachineResource) Arguments() map[string]*pluginsdk.Schema { return map[string]*pluginsdk.Schema{ "arc_machine_id": commonschema.ResourceIDReferenceRequiredForceNew(&machines.MachineId{}), @@ -448,7 +448,38 @@ func (StackHCIVirtualMachineResource) Arguments() map[string]*pluginsdk.Schema { ForceNew: true, MinItems: 1, Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{}, + Schema: map[string]*pluginsdk.Schema{ + "http_proxy": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "https_proxy": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "no_proxy": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + + "trusted_ca": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, }, }, @@ -487,18 +518,24 @@ func (r StackHCIVirtualMachineResource) Create() sdk.ResourceFunc { return metadata.ResourceRequiresImport(r.ResourceType(), id) } + expandedIdentity, err := identity.ExpandSystemAssignedFromModel(config.Identity) + if err != nil { + return fmt.Errorf("expanding `identity`: %+v", err) + } + payload := virtualmachineinstances.VirtualMachineInstance{ + Identity: expandedIdentity, ExtendedLocation: &virtualmachineinstances.ExtendedLocation{ Name: pointer.To(config.CustomLocationId), Type: pointer.To(virtualmachineinstances.ExtendedLocationTypesCustomLocation), }, Properties: &virtualmachineinstances.VirtualMachineInstanceProperties{ - HardwareProfile: expandVirtualMachineInstanceHardwareProfile(config.HardwareProfile), - HTTPProxyConfig: nil, - NetworkProfile: nil, - OsProfile: nil, - SecurityProfile: nil, - StorageProfile: nil, + HardwareProfile: expandVirtualMachineHardwareProfile(config.HardwareProfile), + HTTPProxyConfig: expandVirtualMachineHttpProxyConfig(config.HttpProxyConfiguration), + NetworkProfile: expandVirtualMachineNetworkProfile(config.NetworkProfile), + OsProfile: expandVirtualMachineOsProfile(config.OsProfile), + SecurityProfile: expandVirtualMachineSecurityProfile(config.SecurityProfile), + StorageProfile: expandVirtualMachineStorageProfile(config.StorageProfile), }, } @@ -553,12 +590,12 @@ func (r StackHCIVirtualMachineResource) Read() sdk.ResourceFunc { } if props := model.Properties; props != nil { - schema.HardwareProfile = flattenVirtualMachineInstanceHardwareProfile(props.HardwareProfile) - schema.HttpProxyConfiguration = nil - schema.NetworkProfile = nil - schema.OsProfile = nil - schema.SecurityProfile = nil - schema.StorageProfile = nil + schema.HardwareProfile = flattenVirtualMachineHardwareProfile(props.HardwareProfile) + schema.HttpProxyConfiguration = flattenVirtualMachineHttpProxyConfig(props.HTTPProxyConfig) + schema.NetworkProfile = flattenVirtualMachineNetworkProfile(props.NetworkProfile) + schema.OsProfile = flattenVirtualMachineOsProfile(props.OsProfile) + schema.SecurityProfile = flattenVirtualMachineSecurityProfile(props.SecurityProfile) + schema.StorageProfile = flattenVirtualMachineStorageProfile(props.StorageProfile) } } @@ -581,14 +618,52 @@ func (r StackHCIVirtualMachineResource) Update() sdk.ResourceFunc { arcMachineId := machines.NewMachineID(id.SubscriptionId, id.ResourceGroup, id.MachineName) scopeId := commonids.NewScopeID(arcMachineId.String()) - var model StackHCIVirtualMachineResourceModel - if err := metadata.Decode(&model); err != nil { + var config StackHCIVirtualMachineResourceModel + if err := metadata.Decode(&config); err != nil { return fmt.Errorf("decoding: %+v", err) } parameters := virtualmachineinstances.VirtualMachineInstanceUpdateRequest{} - if metadata.ResourceData.HasChange("") { + if metadata.ResourceData.HasChange("identity") { + expandedIdentity, err := identity.ExpandSystemAssignedFromModel(config.Identity) + if err != nil { + return fmt.Errorf("expanding `identity`: %+v", err) + } + + parameters.Identity = expandedIdentity + } + + if metadata.ResourceData.HasChange("hardware_profile") { + if parameters.Properties == nil { + parameters.Properties = &virtualmachineinstances.VirtualMachineInstanceUpdateProperties{} + } + + parameters.Properties.HardwareProfile = expandVirtualMachineHardwareProfileUpdate(config.HardwareProfile) + } + + if metadata.ResourceData.HasChange("network_profile") { + if parameters.Properties == nil { + parameters.Properties = &virtualmachineinstances.VirtualMachineInstanceUpdateProperties{} + } + + parameters.Properties.NetworkProfile = expandVirtualMachineNetworkProfileUpdate(config.NetworkProfile) + } + + if metadata.ResourceData.HasChange("storage_profile") { + if parameters.Properties == nil { + parameters.Properties = &virtualmachineinstances.VirtualMachineInstanceUpdateProperties{} + } + + parameters.Properties.StorageProfile = expandVirtualMachineStorageProfileUpdate(config.StorageProfile) + } + + if metadata.ResourceData.HasChange("os_profile") { + if parameters.Properties == nil { + parameters.Properties = &virtualmachineinstances.VirtualMachineInstanceUpdateProperties{} + } + + parameters.Properties.OsProfile = expandVirtualMachineOsProfileUpdate(config.OsProfile) } if err := client.UpdateThenPoll(ctx, scopeId, parameters); err != nil { @@ -622,14 +697,14 @@ func (r StackHCIVirtualMachineResource) Delete() sdk.ResourceFunc { } } -func expandVirtualMachineInstanceHardwareProfile(input []StackHCIVirtualMachineHardwareProfile) *virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfile { +func expandVirtualMachineHardwareProfile(input []StackHCIVirtualMachineHardwareProfile) *virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfile { if len(input) == 0 { return nil } v := input[0] output := &virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfile{ - DynamicMemoryConfig: expandVirtualMachineInstanceDynamicMemory(v.DynamicMemory), + DynamicMemoryConfig: expandVirtualMachineDynamicMemory(v.DynamicMemory), MemoryMB: pointer.To(v.MemoryMb), Processors: pointer.To(v.ProcessorNumber), VMSize: pointer.To(virtualmachineinstances.VMSizeEnum(v.VmSize)), @@ -638,7 +713,22 @@ func expandVirtualMachineInstanceHardwareProfile(input []StackHCIVirtualMachineH return output } -func flattenVirtualMachineInstanceHardwareProfile(input *virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfile) []StackHCIVirtualMachineHardwareProfile { +func expandVirtualMachineHardwareProfileUpdate(input []StackHCIVirtualMachineHardwareProfile) *virtualmachineinstances.HardwareProfileUpdate { + if len(input) == 0 { + return nil + } + + v := input[0] + output := &virtualmachineinstances.HardwareProfileUpdate{ + MemoryMB: pointer.To(v.MemoryMb), + Processors: pointer.To(v.ProcessorNumber), + VMSize: pointer.To(virtualmachineinstances.VMSizeEnum(v.VmSize)), + } + + return output +} + +func flattenVirtualMachineHardwareProfile(input *virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfile) []StackHCIVirtualMachineHardwareProfile { if input == nil { return make([]StackHCIVirtualMachineHardwareProfile, 0) } @@ -653,7 +743,7 @@ func flattenVirtualMachineInstanceHardwareProfile(input *virtualmachineinstances } } -func expandVirtualMachineInstanceDynamicMemory(input []StackHCIVirtualMachineDynamicMemory) *virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfileDynamicMemoryConfig { +func expandVirtualMachineDynamicMemory(input []StackHCIVirtualMachineDynamicMemory) *virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfileDynamicMemoryConfig { if len(input) == 0 { return nil } @@ -667,7 +757,7 @@ func expandVirtualMachineInstanceDynamicMemory(input []StackHCIVirtualMachineDyn return output } -func flattenVirtualMachineInstanceDynamicMemory(input *virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfileDynamicMemoryConfig) []StackHCIVirtualMachineDynamicMemory { +func flattenVirtualMachineDynamicMemory(input *virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfileDynamicMemoryConfig) []StackHCIVirtualMachineDynamicMemory { if input == nil { return make([]StackHCIVirtualMachineDynamicMemory, 0) } @@ -679,3 +769,403 @@ func flattenVirtualMachineInstanceDynamicMemory(input *virtualmachineinstances.V }, } } + +func expandVirtualMachineHttpProxyConfig(input []StackHCIVirtualMachineHttpProxyConfiguration) *virtualmachineinstances.HTTPProxyConfiguration { + if len(input) == 0 { + return nil + } + + v := input[0] + output := &virtualmachineinstances.HTTPProxyConfiguration{ + HTTPProxy: pointer.To(v.HttpProxy), + HTTPSProxy: pointer.To(v.HttpsProxy), + NoProxy: pointer.To(v.NoProxy), + TrustedCa: pointer.To(v.TrustedCa), + } + + return output +} + +func flattenVirtualMachineHttpProxyConfig(input *virtualmachineinstances.HTTPProxyConfiguration) []StackHCIVirtualMachineHttpProxyConfiguration { + if input == nil { + return make([]StackHCIVirtualMachineHttpProxyConfiguration, 0) + } + + return []StackHCIVirtualMachineHttpProxyConfiguration{ + { + HttpProxy: pointer.From(input.HTTPProxy), + HttpsProxy: pointer.From(input.HTTPSProxy), + NoProxy: pointer.From(input.NoProxy), + TrustedCa: pointer.From(input.TrustedCa), + }, + } +} + +func expandVirtualMachineNetworkProfile(input []StackHCIVirtualMachineNetworkProfile) *virtualmachineinstances.VirtualMachineInstancePropertiesNetworkProfile { + if len(input) == 0 { + return nil + } + + networkInterfaces := make([]virtualmachineinstances.VirtualMachineInstancePropertiesNetworkProfileNetworkInterfacesInlined, 0) + for _, networkInterfaceId := range input[0].NetworkInterfaceIds { + networkInterfaces = append(networkInterfaces, virtualmachineinstances.VirtualMachineInstancePropertiesNetworkProfileNetworkInterfacesInlined{ + Id: pointer.To(networkInterfaceId), + }) + } + + output := &virtualmachineinstances.VirtualMachineInstancePropertiesNetworkProfile{ + NetworkInterfaces: &networkInterfaces, + } + + return output +} + +func expandVirtualMachineNetworkProfileUpdate(input []StackHCIVirtualMachineNetworkProfile) *virtualmachineinstances.NetworkProfileUpdate { + if len(input) == 0 { + return nil + } + + networkInterfaces := make([]virtualmachineinstances.NetworkProfileUpdateNetworkInterfacesInlined, 0) + for _, networkInterfaceId := range input[0].NetworkInterfaceIds { + networkInterfaces = append(networkInterfaces, virtualmachineinstances.NetworkProfileUpdateNetworkInterfacesInlined{ + Id: pointer.To(networkInterfaceId), + }) + } + + output := &virtualmachineinstances.NetworkProfileUpdate{ + NetworkInterfaces: &networkInterfaces, + } + + return output +} + +func flattenVirtualMachineNetworkProfile(input *virtualmachineinstances.VirtualMachineInstancePropertiesNetworkProfile) []StackHCIVirtualMachineNetworkProfile { + if input == nil || input.NetworkInterfaces == nil { + return make([]StackHCIVirtualMachineNetworkProfile, 0) + } + + networkInterfaceIds := make([]string, 0) + for _, networkInterface := range *input.NetworkInterfaces { + if networkInterface.Id != nil { + networkInterfaceIds = append(networkInterfaceIds, *networkInterface.Id) + } + } + + return []StackHCIVirtualMachineNetworkProfile{ + { + NetworkInterfaceIds: networkInterfaceIds, + }, + } +} + +func expandVirtualMachineOsProfile(input []StackHCIVirtualMachineOsProfile) *virtualmachineinstances.VirtualMachineInstancePropertiesOsProfile { + if len(input) == 0 { + return nil + } + + v := input[0] + output := &virtualmachineinstances.VirtualMachineInstancePropertiesOsProfile{ + AdminUsername: pointer.To(v.AdminUsername), + AdminPassword: pointer.To(v.AdminPassword), + ComputerName: pointer.To(v.ComputerName), + LinuxConfiguration: expandVirtualMachineOsProfileLinuxConfiguration(v.LinuxConfiguration), + WindowsConfiguration: expandVirtualMachineOsProfileWindowsConfiguration(v.WindowsConfiguration), + } + + return output +} + +func expandVirtualMachineOsProfileUpdate(input []StackHCIVirtualMachineOsProfile) *virtualmachineinstances.OsProfileUpdate { + if len(input) == 0 { + return nil + } + + v := input[0] + output := &virtualmachineinstances.OsProfileUpdate{ + ComputerName: pointer.To(v.ComputerName), + LinuxConfiguration: expandVirtualMachineOsProfileLinuxConfigurationUpdate(v.LinuxConfiguration), + WindowsConfiguration: expandVirtualMachineOsProfileWindowsConfigurationUpdate(v.WindowsConfiguration), + } + + return output +} + +func flattenVirtualMachineOsProfile(input *virtualmachineinstances.VirtualMachineInstancePropertiesOsProfile) []StackHCIVirtualMachineOsProfile { + if input == nil { + return make([]StackHCIVirtualMachineOsProfile, 0) + } + + return []StackHCIVirtualMachineOsProfile{ + { + AdminUsername: pointer.From(input.AdminUsername), + AdminPassword: pointer.From(input.AdminPassword), + ComputerName: pointer.From(input.ComputerName), + LinuxConfiguration: flattenVirtualMachineOsProfileLinuxConfiguration(input.LinuxConfiguration), + WindowsConfiguration: flattenVirtualMachineOsProfileWindowsConfiguration(input.WindowsConfiguration), + }, + } +} + +func expandVirtualMachineOsProfileLinuxConfiguration(input []StackHCIVirtualMachineLinuxConfiguration) *virtualmachineinstances.VirtualMachineInstancePropertiesOsProfileLinuxConfiguration { + if len(input) == 0 { + return nil + } + + v := input[0] + output := &virtualmachineinstances.VirtualMachineInstancePropertiesOsProfileLinuxConfiguration{ + DisablePasswordAuthentication: pointer.To(!v.PasswordAuthenticationEnabled), + ProvisionVMAgent: pointer.To(v.ProvisionVmAgentEnabled), + ProvisionVMConfigAgent: pointer.To(v.ProvisionVmConfigAgentEnabled), + Ssh: expandVirtualMachineOsProfileSsh(v.SshPublicKey), + } + + return output +} + +func expandVirtualMachineOsProfileLinuxConfigurationUpdate(input []StackHCIVirtualMachineLinuxConfiguration) *virtualmachineinstances.OsProfileUpdateLinuxConfiguration { + if len(input) == 0 { + return nil + } + + v := input[0] + output := &virtualmachineinstances.OsProfileUpdateLinuxConfiguration{ + ProvisionVMAgent: pointer.To(v.ProvisionVmAgentEnabled), + ProvisionVMConfigAgent: pointer.To(v.ProvisionVmConfigAgentEnabled), + } + + return output +} + +func flattenVirtualMachineOsProfileLinuxConfiguration(input *virtualmachineinstances.VirtualMachineInstancePropertiesOsProfileLinuxConfiguration) []StackHCIVirtualMachineLinuxConfiguration { + if input == nil { + return make([]StackHCIVirtualMachineLinuxConfiguration, 0) + } + + return []StackHCIVirtualMachineLinuxConfiguration{ + { + PasswordAuthenticationEnabled: !pointer.From(input.DisablePasswordAuthentication), + ProvisionVmAgentEnabled: pointer.From(input.ProvisionVMAgent), + ProvisionVmConfigAgentEnabled: pointer.From(input.ProvisionVMConfigAgent), + SshPublicKey: flattenVirtualMachineOsProfileSsh(input.Ssh), + }, + } +} + +func expandVirtualMachineOsProfileWindowsConfiguration(input []StackHCIVirtualMachineWindowsConfiguration) *virtualmachineinstances.VirtualMachineInstancePropertiesOsProfileWindowsConfiguration { + if len(input) == 0 { + return nil + } + + v := input[0] + output := &virtualmachineinstances.VirtualMachineInstancePropertiesOsProfileWindowsConfiguration{ + EnableAutomaticUpdates: pointer.To(v.AutomaticUpdateEnabled), + ProvisionVMAgent: pointer.To(v.ProvisionVmAgentEnabled), + ProvisionVMConfigAgent: pointer.To(v.ProvisionVmConfigAgentEnabled), + Ssh: expandVirtualMachineOsProfileSsh(v.SshPublicKey), + TimeZone: pointer.To(v.TimeZone), + } + + return output +} + +func expandVirtualMachineOsProfileWindowsConfigurationUpdate(input []StackHCIVirtualMachineWindowsConfiguration) *virtualmachineinstances.OsProfileUpdateWindowsConfiguration { + if len(input) == 0 { + return nil + } + + v := input[0] + output := &virtualmachineinstances.OsProfileUpdateWindowsConfiguration{ + ProvisionVMAgent: pointer.To(v.ProvisionVmAgentEnabled), + ProvisionVMConfigAgent: pointer.To(v.ProvisionVmConfigAgentEnabled), + } + + return output +} + +func flattenVirtualMachineOsProfileWindowsConfiguration(input *virtualmachineinstances.VirtualMachineInstancePropertiesOsProfileWindowsConfiguration) []StackHCIVirtualMachineWindowsConfiguration { + if input == nil { + return make([]StackHCIVirtualMachineWindowsConfiguration, 0) + } + + return []StackHCIVirtualMachineWindowsConfiguration{ + { + AutomaticUpdateEnabled: pointer.From(input.EnableAutomaticUpdates), + ProvisionVmAgentEnabled: pointer.From(input.ProvisionVMAgent), + ProvisionVmConfigAgentEnabled: pointer.From(input.ProvisionVMConfigAgent), + SshPublicKey: flattenVirtualMachineOsProfileSsh(input.Ssh), + TimeZone: pointer.From(input.TimeZone), + }, + } +} + +func expandVirtualMachineOsProfileSsh(input []StackHCIVirtualMachineSshPublicKey) *virtualmachineinstances.SshConfiguration { + if len(input) == 0 { + return nil + } + + sshPublicKeys := make([]virtualmachineinstances.SshPublicKey, 0) + for _, key := range input { + sshPublicKeys = append(sshPublicKeys, virtualmachineinstances.SshPublicKey{ + KeyData: pointer.To(key.KeyData), + Path: pointer.To(key.Path), + }) + } + + return &virtualmachineinstances.SshConfiguration{ + PublicKeys: &sshPublicKeys, + } +} + +func flattenVirtualMachineOsProfileSsh(input *virtualmachineinstances.SshConfiguration) []StackHCIVirtualMachineSshPublicKey { + if input == nil || input.PublicKeys == nil { + return make([]StackHCIVirtualMachineSshPublicKey, 0) + } + + output := make([]StackHCIVirtualMachineSshPublicKey, 0) + for _, key := range *input.PublicKeys { + output = append(output, StackHCIVirtualMachineSshPublicKey{ + KeyData: pointer.From(key.KeyData), + Path: pointer.From(key.Path), + }) + } + + return output +} + +func expandVirtualMachineSecurityProfile(input []StackHCIVirtualMachineSecurityProfile) *virtualmachineinstances.VirtualMachineInstancePropertiesSecurityProfile { + if len(input) == 0 { + return nil + } + + v := input[0] + output := &virtualmachineinstances.VirtualMachineInstancePropertiesSecurityProfile{ + EnableTPM: pointer.To(v.TpmEnabled), + SecurityType: pointer.To(virtualmachineinstances.SecurityTypes(v.SecurityType)), + UefiSettings: &virtualmachineinstances.VirtualMachineInstancePropertiesSecurityProfileUefiSettings{ + SecureBootEnabled: pointer.To(v.SecureBootEnabled), + }, + } + + return output +} + +func flattenVirtualMachineSecurityProfile(input *virtualmachineinstances.VirtualMachineInstancePropertiesSecurityProfile) []StackHCIVirtualMachineSecurityProfile { + if input == nil { + return make([]StackHCIVirtualMachineSecurityProfile, 0) + } + + securityProfile := StackHCIVirtualMachineSecurityProfile{ + TpmEnabled: pointer.From(input.EnableTPM), + SecurityType: string(pointer.From(input.SecurityType)), + SecureBootEnabled: false, + } + + if input.UefiSettings != nil { + securityProfile.SecureBootEnabled = pointer.From(input.UefiSettings.SecureBootEnabled) + } + + return []StackHCIVirtualMachineSecurityProfile{ + securityProfile, + } +} + +func expandVirtualMachineStorageProfile(input []StackHCIVirtualMachineStorageProfile) *virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfile { + if len(input) == 0 { + return nil + } + + v := input[0] + + dataDiskIds := make([]virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfileDataDisksInlined, 0) + for _, dataDiskId := range v.DataDiskIds { + dataDiskIds = append(dataDiskIds, virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfileDataDisksInlined{ + Id: pointer.To(dataDiskId), + }) + } + + output := &virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfile{ + DataDisks: pointer.To(dataDiskIds), + OsDisk: expandVirtualMachineOsDisk(v.OsDisk), + VMConfigStoragePathId: pointer.To(v.VmConfigStoragePathId), + ImageReference: &virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfileImageReference{ + Id: pointer.To(v.ImageId), + }, + } + + return output +} + +func expandVirtualMachineStorageProfileUpdate(input []StackHCIVirtualMachineStorageProfile) *virtualmachineinstances.StorageProfileUpdate { + if len(input) == 0 { + return nil + } + + v := input[0] + + dataDiskIds := make([]virtualmachineinstances.StorageProfileUpdateDataDisksInlined, 0) + for _, dataDiskId := range v.DataDiskIds { + dataDiskIds = append(dataDiskIds, virtualmachineinstances.StorageProfileUpdateDataDisksInlined{ + Id: pointer.To(dataDiskId), + }) + } + + output := &virtualmachineinstances.StorageProfileUpdate{ + DataDisks: pointer.To(dataDiskIds), + } + + return output +} + +func flattenVirtualMachineStorageProfile(input *virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfile) []StackHCIVirtualMachineStorageProfile { + if input == nil { + return make([]StackHCIVirtualMachineStorageProfile, 0) + } + + dataDiskIds := make([]string, 0) + if input.DataDisks != nil { + for _, dataDisk := range *input.DataDisks { + if dataDisk.Id != nil { + dataDiskIds = append(dataDiskIds, *dataDisk.Id) + } + } + } + + var imageId string + if input.ImageReference != nil { + imageId = pointer.From(input.ImageReference.Id) + } + + return []StackHCIVirtualMachineStorageProfile{ + { + DataDiskIds: dataDiskIds, + ImageId: imageId, + OsDisk: flattenVirtualMachineOsDisk(input.OsDisk), + VmConfigStoragePathId: pointer.From(input.VMConfigStoragePathId), + }, + } +} + +func expandVirtualMachineOsDisk(input []StackHCIVirtualMachineOsDisk) *virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfileOsDisk { + if len(input) == 0 { + return nil + } + + v := input[0] + return &virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfileOsDisk{ + Id: pointer.To(v.DiskId), + OsType: pointer.To(virtualmachineinstances.OperatingSystemTypes(v.OsType)), + } +} + +func flattenVirtualMachineOsDisk(input *virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfileOsDisk) []StackHCIVirtualMachineOsDisk { + if input == nil { + return make([]StackHCIVirtualMachineOsDisk, 0) + } + + return []StackHCIVirtualMachineOsDisk{ + { + DiskId: pointer.From(input.Id), + OsType: string(pointer.From(input.OsType)), + }, + } +} From 3699207e5ff0a808d3fd47ee34b1211392bbb2a7 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Fri, 15 Nov 2024 09:26:52 +0000 Subject: [PATCH 03/16] wip --- .../services/azurestackhci/registration.go | 1 + ...stack_hci_virtual_machine_resource_test.go | 1 - ...k_hci_windows_virtual_machine_resource.go} | 587 +++++------------- ...i_windows_virtual_machine_resource_test.go | 374 +++++++++++ 4 files changed, 545 insertions(+), 418 deletions(-) delete mode 100644 internal/services/azurestackhci/stack_hci_virtual_machine_resource_test.go rename internal/services/azurestackhci/{stack_hci_virtual_machine_resource.go => stack_hci_windows_virtual_machine_resource.go} (58%) create mode 100644 internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go diff --git a/internal/services/azurestackhci/registration.go b/internal/services/azurestackhci/registration.go index 8eb7a187bef0..ad022e6b48f5 100644 --- a/internal/services/azurestackhci/registration.go +++ b/internal/services/azurestackhci/registration.go @@ -58,5 +58,6 @@ func (r Registration) Resources() []sdk.Resource { StackHCINetworkInterfaceResource{}, StackHCIStoragePathResource{}, StackHCIVirtualHardDiskResource{}, + StackHCIWindowsVirtualMachineResource{}, } } diff --git a/internal/services/azurestackhci/stack_hci_virtual_machine_resource_test.go b/internal/services/azurestackhci/stack_hci_virtual_machine_resource_test.go deleted file mode 100644 index 95c7bd9ea399..000000000000 --- a/internal/services/azurestackhci/stack_hci_virtual_machine_resource_test.go +++ /dev/null @@ -1 +0,0 @@ -package azurestackhci_test diff --git a/internal/services/azurestackhci/stack_hci_virtual_machine_resource.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go similarity index 58% rename from internal/services/azurestackhci/stack_hci_virtual_machine_resource.go rename to internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go index acd313ce6685..ae404244e10d 100644 --- a/internal/services/azurestackhci/stack_hci_virtual_machine_resource.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go @@ -3,6 +3,7 @@ package azurestackhci import ( "context" "fmt" + "regexp" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" @@ -16,7 +17,7 @@ import ( "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/virtualharddisks" "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/virtualmachineinstances" "github.com/hashicorp/go-azure-sdk/resource-manager/extendedlocation/2021-08-15/customlocations" - "github.com/hashicorp/go-azure-sdk/resource-manager/hybridcompute/2024-07-10/machines" + "github.com/hashicorp/go-azure-sdk/resource-manager/hybridcompute/2022-11-10/machines" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/services/azurestackhci/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/azurestackhci/validate" @@ -25,32 +26,32 @@ import ( ) var ( - _ sdk.Resource = StackHCIVirtualMachineResource{} - _ sdk.ResourceWithUpdate = StackHCIVirtualMachineResource{} + _ sdk.Resource = StackHCIWindowsVirtualMachineResource{} + _ sdk.ResourceWithUpdate = StackHCIWindowsVirtualMachineResource{} ) -type StackHCIVirtualMachineResource struct{} +type StackHCIWindowsVirtualMachineResource struct{} -func (StackHCIVirtualMachineResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { +func (StackHCIWindowsVirtualMachineResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { return validate.StackHCIVirtualMachineID } -func (StackHCIVirtualMachineResource) ResourceType() string { - return "azurerm_stack_hci_virtual_machine" +func (StackHCIWindowsVirtualMachineResource) ResourceType() string { + return "azurerm_stack_hci_windows_virtual_machine" } -func (StackHCIVirtualMachineResource) ModelObject() interface{} { - return &StackHCIVirtualMachineResourceModel{} +func (StackHCIWindowsVirtualMachineResource) ModelObject() interface{} { + return &StackHCIWindowsVirtualMachineResourceModel{} } -type StackHCIVirtualMachineResourceModel struct { +type StackHCIWindowsVirtualMachineResourceModel struct { ArcMachineId string `tfschema:"arc_machine_id"` CustomLocationId string `tfschema:"custom_location_id"` HardwareProfile []StackHCIVirtualMachineHardwareProfile `tfschema:"hardware_profile"` HttpProxyConfiguration []StackHCIVirtualMachineHttpProxyConfiguration `tfschema:"http_proxy_configuration"` Identity []identity.ModelSystemAssigned `tfschema:"identity"` NetworkProfile []StackHCIVirtualMachineNetworkProfile `tfschema:"network_profile"` - OsProfile []StackHCIVirtualMachineOsProfile `tfschema:"os_profile"` + OsProfile []StackHCIVirtualMachineOsProfileWindows `tfschema:"os_profile"` SecurityProfile []StackHCIVirtualMachineSecurityProfile `tfschema:"security_profile"` StorageProfile []StackHCIVirtualMachineStorageProfile `tfschema:"storage_profile"` } @@ -79,22 +80,12 @@ type StackHCIVirtualMachineNetworkProfile struct { NetworkInterfaceIds []string `tfschema:"network_interface_ids"` } -type StackHCIVirtualMachineOsProfile struct { - AdminUsername string `tfschema:"admin_username"` - AdminPassword string `tfschema:"admin_password"` - ComputerName string `tfschema:"computer_name"` - LinuxConfiguration []StackHCIVirtualMachineLinuxConfiguration `tfschema:"linux_configuration"` - WindowsConfiguration []StackHCIVirtualMachineWindowsConfiguration `tfschema:"windows_configuration"` -} - -type StackHCIVirtualMachineLinuxConfiguration struct { - PasswordAuthenticationEnabled bool `tfschema:"password_authentication_enabled"` - ProvisionVmAgentEnabled bool `tfschema:"provision_vm_agent_enabled"` - ProvisionVmConfigAgentEnabled bool `tfschema:"provision_vm_config_agent_enabled"` - SshPublicKey []StackHCIVirtualMachineSshPublicKey `tfschema:"ssh_public_key"` -} +type StackHCIVirtualMachineOsProfileWindows struct { + AdminUsername string `tfschema:"admin_username"` + AdminPassword string `tfschema:"admin_password"` + ComputerName string `tfschema:"computer_name"` -type StackHCIVirtualMachineWindowsConfiguration struct { + // windowsConfiguration AutomaticUpdateEnabled bool `tfschema:"automatic_update_enabled"` ProvisionVmAgentEnabled bool `tfschema:"provision_vm_agent_enabled"` ProvisionVmConfigAgentEnabled bool `tfschema:"provision_vm_config_agent_enabled"` @@ -114,18 +105,15 @@ type StackHCIVirtualMachineSecurityProfile struct { } type StackHCIVirtualMachineStorageProfile struct { - DataDiskIds []string `tfschema:"data_disk_ids"` - ImageId string `tfschema:"image_id"` - OsDisk []StackHCIVirtualMachineOsDisk `tfschema:"os_disk"` - VmConfigStoragePathId string `tfschema:"vm_config_storage_path_id"` + DataDiskIds []string `tfschema:"data_disk_ids"` + ImageId string `tfschema:"image_id"` + OsDiskId string `tfschema:"os_disk_id"` + VmConfigStoragePathId string `tfschema:"vm_config_storage_path_id"` } -type StackHCIVirtualMachineOsDisk struct { - DiskId string `tfschema:"disk_id"` - OsType string `tfschema:"os_type"` -} +type StackHCIVirtualMachineOsDisk struct{} -func (StackHCIVirtualMachineResource) Arguments() map[string]*pluginsdk.Schema { +func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.Schema { return map[string]*pluginsdk.Schema{ "arc_machine_id": commonschema.ResourceIDReferenceRequiredForceNew(&machines.MachineId{}), @@ -234,122 +222,63 @@ func (StackHCIVirtualMachineResource) Arguments() map[string]*pluginsdk.Schema { }, "computer_name": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile(`^[\-a-zA-Z0-9]{0,15}$`), + "name must begin and end with an alphanumeric character, be between 2 and 64 characters in length and can only contain alphanumeric characters, hyphens, periods or underscores.", + ), }, - "linux_configuration": { - Type: pluginsdk.TypeList, + "automatic_update_enabled": { + Type: pluginsdk.TypeBool, Optional: true, - MaxItems: 1, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "password_authentication_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - ForceNew: true, - Default: false, - }, - - "ssh_public_key": { - Type: pluginsdk.TypeList, - Optional: true, - ForceNew: true, - MinItems: 1, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "path": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - - "key_data": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - }, - }, - }, - - "provision_vm_agent_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - Default: false, - }, - - "provision_vm_config_agent_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - Default: false, - }, - }, - }, + ForceNew: true, + Default: false, }, - "windows_configuration": { + "ssh_public_key": { Type: pluginsdk.TypeList, Optional: true, ForceNew: true, - MaxItems: 1, + MinItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ - "automatic_update_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - ForceNew: true, - Default: false, - }, - - "ssh_public_key": { - Type: pluginsdk.TypeList, - Optional: true, - ForceNew: true, - MinItems: 1, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "path": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - - "key_data": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - }, - }, - }, - - "time_zone": { + "path": { Type: pluginsdk.TypeString, Required: true, ForceNew: true, ValidateFunc: validation.StringIsNotEmpty, }, - "provision_vm_agent_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - Default: false, - }, - - "provision_vm_config_agent_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - Default: false, + "key_data": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, }, }, }, }, + + "time_zone": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "provision_vm_agent_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + + "provision_vm_config_agent_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, }, }, }, @@ -372,7 +301,7 @@ func (StackHCIVirtualMachineResource) Arguments() map[string]*pluginsdk.Schema { Type: pluginsdk.TypeBool, Optional: true, ForceNew: true, - Default: false, + Default: true, }, "security_type": { @@ -408,33 +337,16 @@ func (StackHCIVirtualMachineResource) Arguments() map[string]*pluginsdk.Schema { ValidateFunc: marketplacegalleryimages.ValidateMarketplaceGalleryImageID, }, - "os_disk": { - Type: pluginsdk.TypeList, - Required: true, - ForceNew: true, - MinItems: 1, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "disk_id": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: virtualharddisks.ValidateVirtualHardDiskID, - }, - - "os_type": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(virtualmachineinstances.PossibleValuesForOperatingSystemTypes(), false), - }, - }, - }, + "os_disk_id": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: virtualharddisks.ValidateVirtualHardDiskID, }, "vm_config_storage_path_id": { Type: pluginsdk.TypeString, - Required: true, + Optional: true, ForceNew: true, ValidateFunc: storagecontainers.ValidateStorageContainerID, }, @@ -487,17 +399,17 @@ func (StackHCIVirtualMachineResource) Arguments() map[string]*pluginsdk.Schema { } } -func (StackHCIVirtualMachineResource) Attributes() map[string]*pluginsdk.Schema { +func (StackHCIWindowsVirtualMachineResource) Attributes() map[string]*pluginsdk.Schema { return map[string]*pluginsdk.Schema{} } -func (r StackHCIVirtualMachineResource) Create() sdk.ResourceFunc { +func (r StackHCIWindowsVirtualMachineResource) Create() sdk.ResourceFunc { return sdk.ResourceFunc{ - Timeout: 30 * time.Minute, + Timeout: 90 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.AzureStackHCI.VirtualMachineInstances - var config StackHCIVirtualMachineResourceModel + var config StackHCIWindowsVirtualMachineResourceModel if err := metadata.Decode(&config); err != nil { return fmt.Errorf("decoding: %+v", err) } @@ -518,13 +430,7 @@ func (r StackHCIVirtualMachineResource) Create() sdk.ResourceFunc { return metadata.ResourceRequiresImport(r.ResourceType(), id) } - expandedIdentity, err := identity.ExpandSystemAssignedFromModel(config.Identity) - if err != nil { - return fmt.Errorf("expanding `identity`: %+v", err) - } - payload := virtualmachineinstances.VirtualMachineInstance{ - Identity: expandedIdentity, ExtendedLocation: &virtualmachineinstances.ExtendedLocation{ Name: pointer.To(config.CustomLocationId), Type: pointer.To(virtualmachineinstances.ExtendedLocationTypesCustomLocation), @@ -533,11 +439,17 @@ func (r StackHCIVirtualMachineResource) Create() sdk.ResourceFunc { HardwareProfile: expandVirtualMachineHardwareProfile(config.HardwareProfile), HTTPProxyConfig: expandVirtualMachineHttpProxyConfig(config.HttpProxyConfiguration), NetworkProfile: expandVirtualMachineNetworkProfile(config.NetworkProfile), - OsProfile: expandVirtualMachineOsProfile(config.OsProfile), + OsProfile: expandVirtualMachineOsProfileWindows(config.OsProfile), SecurityProfile: expandVirtualMachineSecurityProfile(config.SecurityProfile), - StorageProfile: expandVirtualMachineStorageProfile(config.StorageProfile), + StorageProfile: expandVirtualMachineStorageProfileWindows(config.StorageProfile), }, } + if len(config.Identity) > 0 { + payload.Identity, err = identity.ExpandSystemAssignedFromModel(config.Identity) + if err != nil { + return fmt.Errorf("expanding `identity`: %+v", err) + } + } if err := client.CreateOrUpdateThenPoll(ctx, scopeId, payload); err != nil { return fmt.Errorf("creating %s: %+v", id, err) @@ -545,12 +457,14 @@ func (r StackHCIVirtualMachineResource) Create() sdk.ResourceFunc { metadata.SetID(id) + time.Sleep(5 * time.Second) + return nil }, } } -func (r StackHCIVirtualMachineResource) Read() sdk.ResourceFunc { +func (r StackHCIWindowsVirtualMachineResource) Read() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { @@ -562,7 +476,7 @@ func (r StackHCIVirtualMachineResource) Read() sdk.ResourceFunc { } arcMachineId := machines.NewMachineID(id.SubscriptionId, id.ResourceGroup, id.MachineName) - scopeId := commonids.NewScopeID(arcMachineId.String()) + scopeId := commonids.NewScopeID(arcMachineId.ID()) resp, err := client.Get(ctx, scopeId) if err != nil { @@ -570,11 +484,11 @@ func (r StackHCIVirtualMachineResource) Read() sdk.ResourceFunc { return metadata.MarkAsGone(id) } - return fmt.Errorf("retrieving %s: %+v", id, err) + return fmt.Errorf("retrieving %s: %+v", *id, err) } - schema := StackHCIVirtualMachineResourceModel{ - ArcMachineId: arcMachineId.String(), + schema := StackHCIWindowsVirtualMachineResourceModel{ + ArcMachineId: arcMachineId.ID(), } if model := resp.Model; model != nil { @@ -593,9 +507,9 @@ func (r StackHCIVirtualMachineResource) Read() sdk.ResourceFunc { schema.HardwareProfile = flattenVirtualMachineHardwareProfile(props.HardwareProfile) schema.HttpProxyConfiguration = flattenVirtualMachineHttpProxyConfig(props.HTTPProxyConfig) schema.NetworkProfile = flattenVirtualMachineNetworkProfile(props.NetworkProfile) - schema.OsProfile = flattenVirtualMachineOsProfile(props.OsProfile) + schema.OsProfile = flattenVirtualMachineOsProfileWindows(props.OsProfile) schema.SecurityProfile = flattenVirtualMachineSecurityProfile(props.SecurityProfile) - schema.StorageProfile = flattenVirtualMachineStorageProfile(props.StorageProfile) + schema.StorageProfile = flattenVirtualMachineStorageProfileWindows(props.StorageProfile) } } @@ -604,9 +518,9 @@ func (r StackHCIVirtualMachineResource) Read() sdk.ResourceFunc { } } -func (r StackHCIVirtualMachineResource) Update() sdk.ResourceFunc { +func (r StackHCIWindowsVirtualMachineResource) Update() sdk.ResourceFunc { return sdk.ResourceFunc{ - Timeout: 30 * time.Minute, + Timeout: 90 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { client := metadata.Client.AzureStackHCI.VirtualMachineInstances @@ -616,65 +530,65 @@ func (r StackHCIVirtualMachineResource) Update() sdk.ResourceFunc { } arcMachineId := machines.NewMachineID(id.SubscriptionId, id.ResourceGroup, id.MachineName) - scopeId := commonids.NewScopeID(arcMachineId.String()) + scopeId := commonids.NewScopeID(arcMachineId.ID()) - var config StackHCIVirtualMachineResourceModel + var config StackHCIWindowsVirtualMachineResourceModel if err := metadata.Decode(&config); err != nil { return fmt.Errorf("decoding: %+v", err) } - parameters := virtualmachineinstances.VirtualMachineInstanceUpdateRequest{} - - if metadata.ResourceData.HasChange("identity") { - expandedIdentity, err := identity.ExpandSystemAssignedFromModel(config.Identity) - if err != nil { - return fmt.Errorf("expanding `identity`: %+v", err) + resp, err := client.Get(ctx, scopeId) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(id) } - parameters.Identity = expandedIdentity + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + if resp.Model == nil || resp.Model.Properties == nil { + return fmt.Errorf("retrieving %s: `model` was nil", *id) } - if metadata.ResourceData.HasChange("hardware_profile") { - if parameters.Properties == nil { - parameters.Properties = &virtualmachineinstances.VirtualMachineInstanceUpdateProperties{} + existing := resp.Model + + if metadata.ResourceData.HasChange("identity") { + if len(config.Identity) != 0 { + expandedIdentity, err := identity.ExpandSystemAssignedFromModel(config.Identity) + if err != nil { + return fmt.Errorf("expanding `identity`: %+v", err) + } + + existing.Identity = expandedIdentity + } else { + existing.Identity = nil } + } - parameters.Properties.HardwareProfile = expandVirtualMachineHardwareProfileUpdate(config.HardwareProfile) + if metadata.ResourceData.HasChange("hardware_profile") { + existing.Properties.HardwareProfile = expandVirtualMachineHardwareProfile(config.HardwareProfile) } if metadata.ResourceData.HasChange("network_profile") { - if parameters.Properties == nil { - parameters.Properties = &virtualmachineinstances.VirtualMachineInstanceUpdateProperties{} - } - - parameters.Properties.NetworkProfile = expandVirtualMachineNetworkProfileUpdate(config.NetworkProfile) + existing.Properties.NetworkProfile = expandVirtualMachineNetworkProfile(config.NetworkProfile) } if metadata.ResourceData.HasChange("storage_profile") { - if parameters.Properties == nil { - parameters.Properties = &virtualmachineinstances.VirtualMachineInstanceUpdateProperties{} - } - - parameters.Properties.StorageProfile = expandVirtualMachineStorageProfileUpdate(config.StorageProfile) + existing.Properties.StorageProfile = expandVirtualMachineStorageProfileWindows(config.StorageProfile) } if metadata.ResourceData.HasChange("os_profile") { - if parameters.Properties == nil { - parameters.Properties = &virtualmachineinstances.VirtualMachineInstanceUpdateProperties{} - } - - parameters.Properties.OsProfile = expandVirtualMachineOsProfileUpdate(config.OsProfile) + existing.Properties.OsProfile = expandVirtualMachineOsProfileWindows(config.OsProfile) } - if err := client.UpdateThenPoll(ctx, scopeId, parameters); err != nil { - return fmt.Errorf("updating %s: %+v", id, err) + if err := client.CreateOrUpdateThenPoll(ctx, scopeId, *existing); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) } return nil }, } } -func (r StackHCIVirtualMachineResource) Delete() sdk.ResourceFunc { +func (r StackHCIWindowsVirtualMachineResource) Delete() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 30 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { @@ -686,10 +600,10 @@ func (r StackHCIVirtualMachineResource) Delete() sdk.ResourceFunc { } arcMachineId := machines.NewMachineID(id.SubscriptionId, id.ResourceGroup, id.MachineName) - scopeId := commonids.NewScopeID(arcMachineId.String()) + scopeId := commonids.NewScopeID(arcMachineId.ID()) if err := client.DeleteThenPoll(ctx, scopeId); err != nil { - return fmt.Errorf("deleting %s: %+v", id, err) + return fmt.Errorf("deleting %s: %+v", *id, err) } return nil @@ -713,21 +627,6 @@ func expandVirtualMachineHardwareProfile(input []StackHCIVirtualMachineHardwareP return output } -func expandVirtualMachineHardwareProfileUpdate(input []StackHCIVirtualMachineHardwareProfile) *virtualmachineinstances.HardwareProfileUpdate { - if len(input) == 0 { - return nil - } - - v := input[0] - output := &virtualmachineinstances.HardwareProfileUpdate{ - MemoryMB: pointer.To(v.MemoryMb), - Processors: pointer.To(v.ProcessorNumber), - VMSize: pointer.To(virtualmachineinstances.VMSizeEnum(v.VmSize)), - } - - return output -} - func flattenVirtualMachineHardwareProfile(input *virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfile) []StackHCIVirtualMachineHardwareProfile { if input == nil { return make([]StackHCIVirtualMachineHardwareProfile, 0) @@ -820,25 +719,6 @@ func expandVirtualMachineNetworkProfile(input []StackHCIVirtualMachineNetworkPro return output } -func expandVirtualMachineNetworkProfileUpdate(input []StackHCIVirtualMachineNetworkProfile) *virtualmachineinstances.NetworkProfileUpdate { - if len(input) == 0 { - return nil - } - - networkInterfaces := make([]virtualmachineinstances.NetworkProfileUpdateNetworkInterfacesInlined, 0) - for _, networkInterfaceId := range input[0].NetworkInterfaceIds { - networkInterfaces = append(networkInterfaces, virtualmachineinstances.NetworkProfileUpdateNetworkInterfacesInlined{ - Id: pointer.To(networkInterfaceId), - }) - } - - output := &virtualmachineinstances.NetworkProfileUpdate{ - NetworkInterfaces: &networkInterfaces, - } - - return output -} - func flattenVirtualMachineNetworkProfile(input *virtualmachineinstances.VirtualMachineInstancePropertiesNetworkProfile) []StackHCIVirtualMachineNetworkProfile { if input == nil || input.NetworkInterfaces == nil { return make([]StackHCIVirtualMachineNetworkProfile, 0) @@ -858,143 +738,49 @@ func flattenVirtualMachineNetworkProfile(input *virtualmachineinstances.VirtualM } } -func expandVirtualMachineOsProfile(input []StackHCIVirtualMachineOsProfile) *virtualmachineinstances.VirtualMachineInstancePropertiesOsProfile { +func expandVirtualMachineOsProfileWindows(input []StackHCIVirtualMachineOsProfileWindows) *virtualmachineinstances.VirtualMachineInstancePropertiesOsProfile { if len(input) == 0 { return nil } v := input[0] output := &virtualmachineinstances.VirtualMachineInstancePropertiesOsProfile{ - AdminUsername: pointer.To(v.AdminUsername), - AdminPassword: pointer.To(v.AdminPassword), - ComputerName: pointer.To(v.ComputerName), - LinuxConfiguration: expandVirtualMachineOsProfileLinuxConfiguration(v.LinuxConfiguration), - WindowsConfiguration: expandVirtualMachineOsProfileWindowsConfiguration(v.WindowsConfiguration), - } - - return output -} - -func expandVirtualMachineOsProfileUpdate(input []StackHCIVirtualMachineOsProfile) *virtualmachineinstances.OsProfileUpdate { - if len(input) == 0 { - return nil - } - - v := input[0] - output := &virtualmachineinstances.OsProfileUpdate{ - ComputerName: pointer.To(v.ComputerName), - LinuxConfiguration: expandVirtualMachineOsProfileLinuxConfigurationUpdate(v.LinuxConfiguration), - WindowsConfiguration: expandVirtualMachineOsProfileWindowsConfigurationUpdate(v.WindowsConfiguration), - } - - return output -} - -func flattenVirtualMachineOsProfile(input *virtualmachineinstances.VirtualMachineInstancePropertiesOsProfile) []StackHCIVirtualMachineOsProfile { - if input == nil { - return make([]StackHCIVirtualMachineOsProfile, 0) - } - - return []StackHCIVirtualMachineOsProfile{ - { - AdminUsername: pointer.From(input.AdminUsername), - AdminPassword: pointer.From(input.AdminPassword), - ComputerName: pointer.From(input.ComputerName), - LinuxConfiguration: flattenVirtualMachineOsProfileLinuxConfiguration(input.LinuxConfiguration), - WindowsConfiguration: flattenVirtualMachineOsProfileWindowsConfiguration(input.WindowsConfiguration), + AdminUsername: pointer.To(v.AdminUsername), + AdminPassword: pointer.To(v.AdminPassword), + ComputerName: pointer.To(v.ComputerName), + WindowsConfiguration: &virtualmachineinstances.VirtualMachineInstancePropertiesOsProfileWindowsConfiguration{ + EnableAutomaticUpdates: pointer.To(v.AutomaticUpdateEnabled), + ProvisionVMAgent: pointer.To(v.ProvisionVmAgentEnabled), + ProvisionVMConfigAgent: pointer.To(v.ProvisionVmConfigAgentEnabled), + Ssh: expandVirtualMachineOsProfileSsh(v.SshPublicKey), + TimeZone: pointer.To(v.TimeZone), }, } -} - -func expandVirtualMachineOsProfileLinuxConfiguration(input []StackHCIVirtualMachineLinuxConfiguration) *virtualmachineinstances.VirtualMachineInstancePropertiesOsProfileLinuxConfiguration { - if len(input) == 0 { - return nil - } - - v := input[0] - output := &virtualmachineinstances.VirtualMachineInstancePropertiesOsProfileLinuxConfiguration{ - DisablePasswordAuthentication: pointer.To(!v.PasswordAuthenticationEnabled), - ProvisionVMAgent: pointer.To(v.ProvisionVmAgentEnabled), - ProvisionVMConfigAgent: pointer.To(v.ProvisionVmConfigAgentEnabled), - Ssh: expandVirtualMachineOsProfileSsh(v.SshPublicKey), - } return output } -func expandVirtualMachineOsProfileLinuxConfigurationUpdate(input []StackHCIVirtualMachineLinuxConfiguration) *virtualmachineinstances.OsProfileUpdateLinuxConfiguration { - if len(input) == 0 { - return nil - } - - v := input[0] - output := &virtualmachineinstances.OsProfileUpdateLinuxConfiguration{ - ProvisionVMAgent: pointer.To(v.ProvisionVmAgentEnabled), - ProvisionVMConfigAgent: pointer.To(v.ProvisionVmConfigAgentEnabled), - } - - return output -} - -func flattenVirtualMachineOsProfileLinuxConfiguration(input *virtualmachineinstances.VirtualMachineInstancePropertiesOsProfileLinuxConfiguration) []StackHCIVirtualMachineLinuxConfiguration { +func flattenVirtualMachineOsProfileWindows(input *virtualmachineinstances.VirtualMachineInstancePropertiesOsProfile) []StackHCIVirtualMachineOsProfileWindows { if input == nil { - return make([]StackHCIVirtualMachineLinuxConfiguration, 0) - } - - return []StackHCIVirtualMachineLinuxConfiguration{ - { - PasswordAuthenticationEnabled: !pointer.From(input.DisablePasswordAuthentication), - ProvisionVmAgentEnabled: pointer.From(input.ProvisionVMAgent), - ProvisionVmConfigAgentEnabled: pointer.From(input.ProvisionVMConfigAgent), - SshPublicKey: flattenVirtualMachineOsProfileSsh(input.Ssh), - }, - } -} - -func expandVirtualMachineOsProfileWindowsConfiguration(input []StackHCIVirtualMachineWindowsConfiguration) *virtualmachineinstances.VirtualMachineInstancePropertiesOsProfileWindowsConfiguration { - if len(input) == 0 { - return nil - } - - v := input[0] - output := &virtualmachineinstances.VirtualMachineInstancePropertiesOsProfileWindowsConfiguration{ - EnableAutomaticUpdates: pointer.To(v.AutomaticUpdateEnabled), - ProvisionVMAgent: pointer.To(v.ProvisionVmAgentEnabled), - ProvisionVMConfigAgent: pointer.To(v.ProvisionVmConfigAgentEnabled), - Ssh: expandVirtualMachineOsProfileSsh(v.SshPublicKey), - TimeZone: pointer.To(v.TimeZone), - } - - return output -} - -func expandVirtualMachineOsProfileWindowsConfigurationUpdate(input []StackHCIVirtualMachineWindowsConfiguration) *virtualmachineinstances.OsProfileUpdateWindowsConfiguration { - if len(input) == 0 { - return nil + return make([]StackHCIVirtualMachineOsProfileWindows, 0) } - v := input[0] - output := &virtualmachineinstances.OsProfileUpdateWindowsConfiguration{ - ProvisionVMAgent: pointer.To(v.ProvisionVmAgentEnabled), - ProvisionVMConfigAgent: pointer.To(v.ProvisionVmConfigAgentEnabled), + result := StackHCIVirtualMachineOsProfileWindows{ + AdminUsername: pointer.From(input.AdminUsername), + AdminPassword: pointer.From(input.AdminPassword), + ComputerName: pointer.From(input.ComputerName), } - return output -} - -func flattenVirtualMachineOsProfileWindowsConfiguration(input *virtualmachineinstances.VirtualMachineInstancePropertiesOsProfileWindowsConfiguration) []StackHCIVirtualMachineWindowsConfiguration { - if input == nil { - return make([]StackHCIVirtualMachineWindowsConfiguration, 0) + if input.WindowsConfiguration != nil { + result.AutomaticUpdateEnabled = pointer.From(input.WindowsConfiguration.EnableAutomaticUpdates) + result.ProvisionVmAgentEnabled = pointer.From(input.WindowsConfiguration.ProvisionVMAgent) + result.ProvisionVmConfigAgentEnabled = pointer.From(input.WindowsConfiguration.ProvisionVMConfigAgent) + result.SshPublicKey = flattenVirtualMachineOsProfileSsh(input.WindowsConfiguration.Ssh) + result.TimeZone = pointer.From(input.WindowsConfiguration.TimeZone) } - return []StackHCIVirtualMachineWindowsConfiguration{ - { - AutomaticUpdateEnabled: pointer.From(input.EnableAutomaticUpdates), - ProvisionVmAgentEnabled: pointer.From(input.ProvisionVMAgent), - ProvisionVmConfigAgentEnabled: pointer.From(input.ProvisionVMConfigAgent), - SshPublicKey: flattenVirtualMachineOsProfileSsh(input.Ssh), - TimeZone: pointer.From(input.TimeZone), - }, + return []StackHCIVirtualMachineOsProfileWindows{ + result, } } @@ -1054,22 +840,21 @@ func flattenVirtualMachineSecurityProfile(input *virtualmachineinstances.Virtual return make([]StackHCIVirtualMachineSecurityProfile, 0) } - securityProfile := StackHCIVirtualMachineSecurityProfile{ - TpmEnabled: pointer.From(input.EnableTPM), - SecurityType: string(pointer.From(input.SecurityType)), - SecureBootEnabled: false, - } - + secureBootEnabled := false if input.UefiSettings != nil { - securityProfile.SecureBootEnabled = pointer.From(input.UefiSettings.SecureBootEnabled) + secureBootEnabled = pointer.From(input.UefiSettings.SecureBootEnabled) } return []StackHCIVirtualMachineSecurityProfile{ - securityProfile, + { + TpmEnabled: pointer.From(input.EnableTPM), + SecurityType: string(pointer.From(input.SecurityType)), + SecureBootEnabled: secureBootEnabled, + }, } } -func expandVirtualMachineStorageProfile(input []StackHCIVirtualMachineStorageProfile) *virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfile { +func expandVirtualMachineStorageProfileWindows(input []StackHCIVirtualMachineStorageProfile) *virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfile { if len(input) == 0 { return nil } @@ -1084,39 +869,27 @@ func expandVirtualMachineStorageProfile(input []StackHCIVirtualMachineStoragePro } output := &virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfile{ - DataDisks: pointer.To(dataDiskIds), - OsDisk: expandVirtualMachineOsDisk(v.OsDisk), - VMConfigStoragePathId: pointer.To(v.VmConfigStoragePathId), + DataDisks: pointer.To(dataDiskIds), + OsDisk: &virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfileOsDisk{ + OsType: pointer.To(virtualmachineinstances.OperatingSystemTypesWindows), + }, ImageReference: &virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfileImageReference{ Id: pointer.To(v.ImageId), }, } - return output -} - -func expandVirtualMachineStorageProfileUpdate(input []StackHCIVirtualMachineStorageProfile) *virtualmachineinstances.StorageProfileUpdate { - if len(input) == 0 { - return nil + if v.OsDiskId != "" { + output.OsDisk.Id = pointer.To(v.OsDiskId) } - v := input[0] - - dataDiskIds := make([]virtualmachineinstances.StorageProfileUpdateDataDisksInlined, 0) - for _, dataDiskId := range v.DataDiskIds { - dataDiskIds = append(dataDiskIds, virtualmachineinstances.StorageProfileUpdateDataDisksInlined{ - Id: pointer.To(dataDiskId), - }) - } - - output := &virtualmachineinstances.StorageProfileUpdate{ - DataDisks: pointer.To(dataDiskIds), + if v.VmConfigStoragePathId != "" { + output.VMConfigStoragePathId = pointer.To(v.VmConfigStoragePathId) } return output } -func flattenVirtualMachineStorageProfile(input *virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfile) []StackHCIVirtualMachineStorageProfile { +func flattenVirtualMachineStorageProfileWindows(input *virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfile) []StackHCIVirtualMachineStorageProfile { if input == nil { return make([]StackHCIVirtualMachineStorageProfile, 0) } @@ -1135,37 +908,17 @@ func flattenVirtualMachineStorageProfile(input *virtualmachineinstances.VirtualM imageId = pointer.From(input.ImageReference.Id) } + var osDiskId string + if input.OsDisk != nil { + osDiskId = pointer.From(input.OsDisk.Id) + } + return []StackHCIVirtualMachineStorageProfile{ { DataDiskIds: dataDiskIds, ImageId: imageId, - OsDisk: flattenVirtualMachineOsDisk(input.OsDisk), + OsDiskId: osDiskId, VmConfigStoragePathId: pointer.From(input.VMConfigStoragePathId), }, } } - -func expandVirtualMachineOsDisk(input []StackHCIVirtualMachineOsDisk) *virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfileOsDisk { - if len(input) == 0 { - return nil - } - - v := input[0] - return &virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfileOsDisk{ - Id: pointer.To(v.DiskId), - OsType: pointer.To(virtualmachineinstances.OperatingSystemTypes(v.OsType)), - } -} - -func flattenVirtualMachineOsDisk(input *virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfileOsDisk) []StackHCIVirtualMachineOsDisk { - if input == nil { - return make([]StackHCIVirtualMachineOsDisk, 0) - } - - return []StackHCIVirtualMachineOsDisk{ - { - DiskId: pointer.From(input.Id), - OsType: string(pointer.From(input.OsType)), - }, - } -} diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go new file mode 100644 index 000000000000..2b0abd9d2ea1 --- /dev/null +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go @@ -0,0 +1,374 @@ +package azurestackhci_test + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" + "github.com/hashicorp/go-azure-sdk/resource-manager/hybridcompute/2024-07-10/machines" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/azurestackhci/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +type StackHCIWindowsVirtualMachineResource struct{} + +func TestAccStackHCIWindowsVirtualMachine_basic(t *testing.T) { + if os.Getenv(customLocationIdEnv) == "" { + t.Skipf("skipping since %q has not been specified", customLocationIdEnv) + } + + data := acceptance.BuildTestData(t, "azurerm_stack_hci_windows_virtual_machine", "test") + r := StackHCIWindowsVirtualMachineResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccStackHCIWindowsVirtualMachine_complete(t *testing.T) { + if os.Getenv(customLocationIdEnv) == "" { + t.Skipf("skipping since %q has not been specified", customLocationIdEnv) + } + + data := acceptance.BuildTestData(t, "azurerm_stack_hci_windows_virtual_machine", "test") + r := StackHCIWindowsVirtualMachineResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccStackHCIWindowsVirtualMachine_update(t *testing.T) { + if os.Getenv(customLocationIdEnv) == "" { + t.Skipf("skipping since %q has not been specified", customLocationIdEnv) + } + + data := acceptance.BuildTestData(t, "azurerm_stack_hci_windows_virtual_machine", "test") + r := StackHCIWindowsVirtualMachineResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.updateTag(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccStackHCIWindowsVirtualMachine_requiresImport(t *testing.T) { + if os.Getenv(customLocationIdEnv) == "" { + t.Skipf("skipping since %q has not been specified", customLocationIdEnv) + } + + data := acceptance.BuildTestData(t, "azurerm_stack_hci_windows_virtual_machine", "test") + r := StackHCIWindowsVirtualMachineResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func (r StackHCIWindowsVirtualMachineResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + clusterClient := client.AzureStackHCI.VirtualMachineInstances + id, err := parse.StackHCIVirtualMachineID(state.ID) + if err != nil { + return nil, err + } + + arcMachineId := machines.NewMachineID(id.SubscriptionId, id.ResourceGroup, id.MachineName) + scopeId := commonids.NewScopeID(arcMachineId.ID()) + resp, err := clusterClient.Get(ctx, scopeId) + if err != nil { + return nil, fmt.Errorf("retrieving %s: %+v", id, err) + } + + return pointer.To(resp.Model != nil), nil +} + +func (r StackHCIWindowsVirtualMachineResource) basic(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +provider "azurerm" { + features {} +} + +resource "azurerm_stack_hci_windows_virtual_machine" "test" { + arc_machine_id = azurerm_arc_machine.test.id + custom_location_id = %[3]q + + hardware_profile { + vm_size = "Custom" + processor_number = 2 + memory_mb = 8192 + } + + network_profile { + network_interface_ids = [azurerm_stack_hci_network_interface.test.id] + } + + os_profile { + admin_username = "adminuser" + admin_password = "!password!@#$" + computer_name = "1a" + } + + storage_profile { + data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id] + image_id = azurerm_stack_hci_marketplace_gallery_image.test.id + } + + depends_on = [azurerm_role_assignment.test] +} +`, template, data.RandomString, os.Getenv(customLocationIdEnv)) +} + +func (r StackHCIWindowsVirtualMachineResource) update(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +provider "azurerm" { + features {} +} + +resource "azurerm_stack_hci_virtual_hard_disk" "test" { + name = "acctest-vhd-%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = %q + disk_size_in_gb = 2 + + tags = { + foo = "bar" + } + + lifecycle { + ignore_changes = [storage_path_id] + } +} +`, template, data.RandomString, os.Getenv(customLocationIdEnv)) +} + +func (r StackHCIWindowsVirtualMachineResource) updateTag(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +provider "azurerm" { + features {} +} + +resource "azurerm_stack_hci_virtual_hard_disk" "test" { + name = "acctest-vhd-%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = %q + disk_size_in_gb = 2 + + tags = { + env = "test" + foo = "bar" + } + + lifecycle { + ignore_changes = [storage_path_id] + } +} +`, template, data.RandomString, os.Getenv(customLocationIdEnv)) +} + +func (r StackHCIWindowsVirtualMachineResource) complete(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%[1]s + +provider "azurerm" { + features {} +} + +resource "azurerm_stack_hci_storage_path" "test" { + name = "acctest-sp-%[2]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = %[3]q + path = "C:\\ClusterStorage\\UserStorage_2\\sp-%[2]s" +} + +resource "azurerm_stack_hci_virtual_hard_disk" "test" { + name = "acctest-vhd-%[2]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = %[3]q + disk_size_in_gb = 2 + dynamic_enabled = false + hyperv_generation = "V2" + physical_sector_in_bytes = 4096 + logical_sector_in_bytes = 512 + block_size_in_bytes = 1024 + disk_file_format = "vhdx" + storage_path_id = azurerm_stack_hci_storage_path.test.id + + tags = { + foo = "bar" + env = "test" + } +} +`, template, data.RandomString, os.Getenv(customLocationIdEnv)) +} + +func (r StackHCIWindowsVirtualMachineResource) requiresImport(data acceptance.TestData) string { + config := r.basic(data) + + return fmt.Sprintf(` +%s + +resource "azurerm_stack_hci_virtual_hard_disk" "import" { + name = azurerm_stack_hci_virtual_hard_disk.test.name + resource_group_name = azurerm_stack_hci_virtual_hard_disk.test.resource_group_name + location = azurerm_stack_hci_virtual_hard_disk.test.location + custom_location_id = azurerm_stack_hci_virtual_hard_disk.test.custom_location_id + disk_size_in_gb = azurerm_stack_hci_virtual_hard_disk.test.disk_size_in_gb +} +`, config) +} + +func (r StackHCIWindowsVirtualMachineResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctest-hci-vm-%[2]s" + location = %[1]q +} + +resource "azurerm_stack_hci_logical_network" "test" { + name = "acctest-ln-%[2]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = %[3]q + virtual_switch_name = "ConvergedSwitch(managementcompute)" + + subnet { + ip_allocation_method = "Dynamic" + } +} + +resource "azurerm_stack_hci_network_interface" "test" { + name = "acctest-ni-%[2]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = %[3]q + + ip_configuration { + subnet_id = azurerm_stack_hci_logical_network.test.id + } + + lifecycle { + ignore_changes = [mac_address] + } +} + +// service principal of 'Microsoft.AzureStackHCI Resource Provider' +data "azuread_service_principal" "hciRp" { + client_id = "1412d89f-b8a8-4111-b4fd-e82905cbd85d" +} + +resource "azurerm_role_assignment" "test" { + scope = azurerm_resource_group.test.id + role_definition_name = "Azure Connected Machine Resource Manager" + principal_id = data.azuread_service_principal.hciRp.object_id +} + +//resource "azurerm_stack_hci_marketplace_gallery_image" "test" { +// name = "acctest-mgi-%[2]s" +// resource_group_name = azurerm_resource_group.test.name +// location = azurerm_resource_group.test.location +// custom_location_id = %[3]q +// hyperv_generation = "V2" +// os_type = "Windows" +// version = "20348.2655.240905" +// identifier { +// publisher = "MicrosoftWindowsServer" +// offer = "WindowsServer" +// sku = "2022-datacenter-azure-edition-core" +// } +// +// depends_on = [azurerm_role_assignment.test] +//} + +resource "azurerm_stack_hci_virtual_hard_disk" "test" { + name = "acctest-vhd-%[2]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = %[3]q + disk_size_in_gb = 2 + + tags = { + env = "test" + foo = "bar" + } + + lifecycle { + ignore_changes = [storage_path_id] + } +} + +resource "azurerm_arc_machine" "test" { + name = "acctest-hcivm-%[2]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + kind = "HCI" + + //identity { + // type = "SystemAssigned" + //} +} +`, data.Locations.Primary, data.RandomString, os.Getenv(customLocationIdEnv)) +} From 48cc23bfc83fc9f00fc5e0c57e098adf38893b6b Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Mon, 9 Dec 2024 01:57:18 +0000 Subject: [PATCH 04/16] wip --- ...ck_hci_windows_virtual_machine_resource.go | 262 +++++++++-------- ...i_windows_virtual_machine_resource_test.go | 265 ++++++++++++------ 2 files changed, 308 insertions(+), 219 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go index ae404244e10d..aabd605ad89d 100644 --- a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go @@ -10,7 +10,6 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" - "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/marketplacegalleryimages" "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/networkinterfaces" "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/storagecontainers" @@ -49,11 +48,14 @@ type StackHCIWindowsVirtualMachineResourceModel struct { CustomLocationId string `tfschema:"custom_location_id"` HardwareProfile []StackHCIVirtualMachineHardwareProfile `tfschema:"hardware_profile"` HttpProxyConfiguration []StackHCIVirtualMachineHttpProxyConfiguration `tfschema:"http_proxy_configuration"` - Identity []identity.ModelSystemAssigned `tfschema:"identity"` NetworkProfile []StackHCIVirtualMachineNetworkProfile `tfschema:"network_profile"` OsProfile []StackHCIVirtualMachineOsProfileWindows `tfschema:"os_profile"` - SecurityProfile []StackHCIVirtualMachineSecurityProfile `tfschema:"security_profile"` StorageProfile []StackHCIVirtualMachineStorageProfile `tfschema:"storage_profile"` + + // securityProfile + SecureBootEnabled bool `tfschema:"secure_boot_enabled"` + SecurityType string `tfschema:"security_type"` + TpmEnabled bool `tfschema:"tpm_enabled"` } type StackHCIVirtualMachineHardwareProfile struct { @@ -98,12 +100,6 @@ type StackHCIVirtualMachineSshPublicKey struct { Path string `tfschema:"path"` } -type StackHCIVirtualMachineSecurityProfile struct { - SecureBootEnabled bool `tfschema:"secure_boot_enabled"` - SecurityType string `tfschema:"security_type"` - TpmEnabled bool `tfschema:"tpm_enabled"` -} - type StackHCIVirtualMachineStorageProfile struct { DataDiskIds []string `tfschema:"data_disk_ids"` ImageId string `tfschema:"image_id"` @@ -172,7 +168,7 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S Type: pluginsdk.TypeInt, Required: true, ForceNew: true, - ValidateFunc: validation.IntAtLeast(1), + ValidateFunc: validation.IntBetween(5, 2000), }, }, }, @@ -203,6 +199,7 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S "os_profile": { Type: pluginsdk.TypeList, Required: true, + ForceNew: true, MaxItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ @@ -224,9 +221,10 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S "computer_name": { Type: pluginsdk.TypeString, Required: true, + ForceNew: true, ValidateFunc: validation.StringMatch( regexp.MustCompile(`^[\-a-zA-Z0-9]{0,15}$`), - "name must begin and end with an alphanumeric character, be between 2 and 64 characters in length and can only contain alphanumeric characters, hyphens, periods or underscores.", + "computer_name must begin and end with an alphanumeric character, be between 2 and 15 characters in length and can only contain alphanumeric characters and hyphens.", ), }, @@ -241,7 +239,6 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S Type: pluginsdk.TypeList, Optional: true, ForceNew: true, - MinItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "path": { @@ -271,53 +268,45 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S "provision_vm_agent_enabled": { Type: pluginsdk.TypeBool, Optional: true, + ForceNew: true, Default: false, }, "provision_vm_config_agent_enabled": { Type: pluginsdk.TypeBool, Optional: true, + ForceNew: true, Default: false, }, }, }, }, - "security_profile": { - Type: pluginsdk.TypeList, + "tpm_enabled": { + Type: pluginsdk.TypeBool, Optional: true, ForceNew: true, - MinItems: 1, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "tpm_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - ForceNew: true, - Default: false, - }, + Default: false, + }, - "secure_boot_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - ForceNew: true, - Default: true, - }, + "secure_boot_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + Default: true, + }, - "security_type": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice(virtualmachineinstances.PossibleValuesForSecurityTypes(), false), - }, - }, - }, + "security_type": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(virtualmachineinstances.PossibleValuesForSecurityTypes(), false), }, "storage_profile": { Type: pluginsdk.TypeList, Required: true, - MinItems: 1, + MaxItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "data_disk_ids": { @@ -358,13 +347,14 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S Type: pluginsdk.TypeList, Optional: true, ForceNew: true, - MinItems: 1, + MaxItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "http_proxy": { Type: pluginsdk.TypeString, Optional: true, ForceNew: true, + Sensitive: true, ValidateFunc: validation.StringIsNotEmpty, }, @@ -372,6 +362,7 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S Type: pluginsdk.TypeString, Optional: true, ForceNew: true, + Sensitive: true, ValidateFunc: validation.StringIsNotEmpty, }, @@ -394,8 +385,6 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S }, }, }, - - "identity": commonschema.SystemAssignedIdentityOptional(), } } @@ -440,16 +429,10 @@ func (r StackHCIWindowsVirtualMachineResource) Create() sdk.ResourceFunc { HTTPProxyConfig: expandVirtualMachineHttpProxyConfig(config.HttpProxyConfiguration), NetworkProfile: expandVirtualMachineNetworkProfile(config.NetworkProfile), OsProfile: expandVirtualMachineOsProfileWindows(config.OsProfile), - SecurityProfile: expandVirtualMachineSecurityProfile(config.SecurityProfile), + SecurityProfile: expandVirtualMachineSecurityProfile(config), StorageProfile: expandVirtualMachineStorageProfileWindows(config.StorageProfile), }, } - if len(config.Identity) > 0 { - payload.Identity, err = identity.ExpandSystemAssignedFromModel(config.Identity) - if err != nil { - return fmt.Errorf("expanding `identity`: %+v", err) - } - } if err := client.CreateOrUpdateThenPoll(ctx, scopeId, payload); err != nil { return fmt.Errorf("creating %s: %+v", id, err) @@ -457,8 +440,6 @@ func (r StackHCIWindowsVirtualMachineResource) Create() sdk.ResourceFunc { metadata.SetID(id) - time.Sleep(5 * time.Second) - return nil }, } @@ -475,6 +456,11 @@ func (r StackHCIWindowsVirtualMachineResource) Read() sdk.ResourceFunc { return err } + var config StackHCIWindowsVirtualMachineResourceModel + if err := metadata.Decode(&config); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + arcMachineId := machines.NewMachineID(id.SubscriptionId, id.ResourceGroup, id.MachineName) scopeId := commonids.NewScopeID(arcMachineId.ID()) @@ -492,8 +478,6 @@ func (r StackHCIWindowsVirtualMachineResource) Read() sdk.ResourceFunc { } if model := resp.Model; model != nil { - schema.Identity = identity.FlattenSystemAssignedToModel(model.Identity) - if model.ExtendedLocation != nil && model.ExtendedLocation.Name != nil { customLocationId, err := customlocations.ParseCustomLocationIDInsensitively(*model.ExtendedLocation.Name) if err != nil { @@ -505,11 +489,21 @@ func (r StackHCIWindowsVirtualMachineResource) Read() sdk.ResourceFunc { if props := model.Properties; props != nil { schema.HardwareProfile = flattenVirtualMachineHardwareProfile(props.HardwareProfile) - schema.HttpProxyConfiguration = flattenVirtualMachineHttpProxyConfig(props.HTTPProxyConfig) + schema.HttpProxyConfiguration = flattenVirtualMachineHttpProxyConfig(props.HTTPProxyConfig, config.HttpProxyConfiguration) schema.NetworkProfile = flattenVirtualMachineNetworkProfile(props.NetworkProfile) - schema.OsProfile = flattenVirtualMachineOsProfileWindows(props.OsProfile) - schema.SecurityProfile = flattenVirtualMachineSecurityProfile(props.SecurityProfile) + schema.OsProfile = flattenVirtualMachineOsProfileWindows(props.OsProfile, config.OsProfile) schema.StorageProfile = flattenVirtualMachineStorageProfileWindows(props.StorageProfile) + + if securityProfile := props.SecurityProfile; securityProfile != nil { + schema.TpmEnabled = pointer.From(securityProfile.EnableTPM) + schema.SecurityType = string(pointer.From(securityProfile.SecurityType)) + + secureBootEnabled := false + if securityProfile.UefiSettings != nil { + secureBootEnabled = pointer.From(securityProfile.UefiSettings.SecureBootEnabled) + } + schema.SecureBootEnabled = secureBootEnabled + } } } @@ -537,50 +531,19 @@ func (r StackHCIWindowsVirtualMachineResource) Update() sdk.ResourceFunc { return fmt.Errorf("decoding: %+v", err) } - resp, err := client.Get(ctx, scopeId) - if err != nil { - if response.WasNotFound(resp.HttpResponse) { - return metadata.MarkAsGone(id) - } - - return fmt.Errorf("retrieving %s: %+v", *id, err) - } - if resp.Model == nil || resp.Model.Properties == nil { - return fmt.Errorf("retrieving %s: `model` was nil", *id) - } - - existing := resp.Model - - if metadata.ResourceData.HasChange("identity") { - if len(config.Identity) != 0 { - expandedIdentity, err := identity.ExpandSystemAssignedFromModel(config.Identity) - if err != nil { - return fmt.Errorf("expanding `identity`: %+v", err) - } - - existing.Identity = expandedIdentity - } else { - existing.Identity = nil - } - } - - if metadata.ResourceData.HasChange("hardware_profile") { - existing.Properties.HardwareProfile = expandVirtualMachineHardwareProfile(config.HardwareProfile) + payload := virtualmachineinstances.VirtualMachineInstanceUpdateRequest{ + Properties: &virtualmachineinstances.VirtualMachineInstanceUpdateProperties{}, } if metadata.ResourceData.HasChange("network_profile") { - existing.Properties.NetworkProfile = expandVirtualMachineNetworkProfile(config.NetworkProfile) + payload.Properties.NetworkProfile = expandVirtualMachineNetworkProfileForUpdate(config.NetworkProfile) } if metadata.ResourceData.HasChange("storage_profile") { - existing.Properties.StorageProfile = expandVirtualMachineStorageProfileWindows(config.StorageProfile) + payload.Properties.StorageProfile = expandVirtualMachineStorageProfileWindowsForUpdate(config.StorageProfile) } - if metadata.ResourceData.HasChange("os_profile") { - existing.Properties.OsProfile = expandVirtualMachineOsProfileWindows(config.OsProfile) - } - - if err := client.CreateOrUpdateThenPoll(ctx, scopeId, *existing); err != nil { + if err := client.UpdateThenPoll(ctx, scopeId, payload); err != nil { return fmt.Errorf("updating %s: %+v", *id, err) } return nil @@ -634,7 +597,7 @@ func flattenVirtualMachineHardwareProfile(input *virtualmachineinstances.Virtual return []StackHCIVirtualMachineHardwareProfile{ { - DynamicMemory: nil, + DynamicMemory: flattenVirtualMachineDynamicMemory(input.DynamicMemoryConfig), MemoryMb: pointer.From(input.MemoryMB), ProcessorNumber: pointer.From(input.Processors), VmSize: string(pointer.From(input.VMSize)), @@ -649,8 +612,9 @@ func expandVirtualMachineDynamicMemory(input []StackHCIVirtualMachineDynamicMemo v := input[0] output := &virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfileDynamicMemoryConfig{ - MaximumMemoryMB: pointer.To(v.MaximumMemoryMb), - MinimumMemoryMB: pointer.To(v.MinimumMemoryMb), + MaximumMemoryMB: pointer.To(v.MaximumMemoryMb), + MinimumMemoryMB: pointer.To(v.MinimumMemoryMb), + TargetMemoryBuffer: pointer.To(v.TargetMemoryBuffer), } return output @@ -661,10 +625,16 @@ func flattenVirtualMachineDynamicMemory(input *virtualmachineinstances.VirtualMa return make([]StackHCIVirtualMachineDynamicMemory, 0) } + // The API may return blocks with all values set to 0, in this case we ignore the block + if pointer.From(input.MaximumMemoryMB) == 0 && pointer.From(input.MinimumMemoryMB) == 0 && pointer.From(input.TargetMemoryBuffer) == 0 { + return make([]StackHCIVirtualMachineDynamicMemory, 0) + } + return []StackHCIVirtualMachineDynamicMemory{ { - MaximumMemoryMb: pointer.From(input.MaximumMemoryMB), - MinimumMemoryMb: pointer.From(input.MinimumMemoryMB), + MaximumMemoryMb: pointer.From(input.MaximumMemoryMB), + MinimumMemoryMb: pointer.From(input.MinimumMemoryMB), + TargetMemoryBuffer: pointer.From(input.TargetMemoryBuffer), }, } } @@ -685,19 +655,44 @@ func expandVirtualMachineHttpProxyConfig(input []StackHCIVirtualMachineHttpProxy return output } -func flattenVirtualMachineHttpProxyConfig(input *virtualmachineinstances.HTTPProxyConfiguration) []StackHCIVirtualMachineHttpProxyConfiguration { +func flattenVirtualMachineHttpProxyConfig(input *virtualmachineinstances.HTTPProxyConfiguration, configuredHttpProxyConfig []StackHCIVirtualMachineHttpProxyConfiguration) []StackHCIVirtualMachineHttpProxyConfiguration { if input == nil { return make([]StackHCIVirtualMachineHttpProxyConfiguration, 0) } + output := StackHCIVirtualMachineHttpProxyConfiguration{ + NoProxy: pointer.From(input.NoProxy), + TrustedCa: pointer.From(input.TrustedCa), + } + + // httpProxy and httpsProxy are not returned from the server + if len(configuredHttpProxyConfig) > 0 { + output.HttpProxy = configuredHttpProxyConfig[0].HttpProxy + output.HttpsProxy = configuredHttpProxyConfig[0].HttpsProxy + } + return []StackHCIVirtualMachineHttpProxyConfiguration{ - { - HttpProxy: pointer.From(input.HTTPProxy), - HttpsProxy: pointer.From(input.HTTPSProxy), - NoProxy: pointer.From(input.NoProxy), - TrustedCa: pointer.From(input.TrustedCa), - }, + output, + } +} + +func expandVirtualMachineNetworkProfileForUpdate(input []StackHCIVirtualMachineNetworkProfile) *virtualmachineinstances.NetworkProfileUpdate { + if len(input) == 0 { + return nil + } + + networkInterfaces := make([]virtualmachineinstances.NetworkProfileUpdateNetworkInterfacesInlined, 0) + for _, networkInterfaceId := range input[0].NetworkInterfaceIds { + networkInterfaces = append(networkInterfaces, virtualmachineinstances.NetworkProfileUpdateNetworkInterfacesInlined{ + Id: pointer.To(networkInterfaceId), + }) } + + output := &virtualmachineinstances.NetworkProfileUpdate{ + NetworkInterfaces: &networkInterfaces, + } + + return output } func expandVirtualMachineNetworkProfile(input []StackHCIVirtualMachineNetworkProfile) *virtualmachineinstances.VirtualMachineInstancePropertiesNetworkProfile { @@ -760,27 +755,31 @@ func expandVirtualMachineOsProfileWindows(input []StackHCIVirtualMachineOsProfil return output } -func flattenVirtualMachineOsProfileWindows(input *virtualmachineinstances.VirtualMachineInstancePropertiesOsProfile) []StackHCIVirtualMachineOsProfileWindows { +func flattenVirtualMachineOsProfileWindows(input *virtualmachineinstances.VirtualMachineInstancePropertiesOsProfile, configuredOsProfile []StackHCIVirtualMachineOsProfileWindows) []StackHCIVirtualMachineOsProfileWindows { if input == nil { return make([]StackHCIVirtualMachineOsProfileWindows, 0) } - result := StackHCIVirtualMachineOsProfileWindows{ + output := StackHCIVirtualMachineOsProfileWindows{ AdminUsername: pointer.From(input.AdminUsername), - AdminPassword: pointer.From(input.AdminPassword), ComputerName: pointer.From(input.ComputerName), } + // adminPassword is not returned from the server, so it should be taken from the configured value + if len(configuredOsProfile) > 0 { + output.AdminPassword = configuredOsProfile[0].AdminPassword + } + if input.WindowsConfiguration != nil { - result.AutomaticUpdateEnabled = pointer.From(input.WindowsConfiguration.EnableAutomaticUpdates) - result.ProvisionVmAgentEnabled = pointer.From(input.WindowsConfiguration.ProvisionVMAgent) - result.ProvisionVmConfigAgentEnabled = pointer.From(input.WindowsConfiguration.ProvisionVMConfigAgent) - result.SshPublicKey = flattenVirtualMachineOsProfileSsh(input.WindowsConfiguration.Ssh) - result.TimeZone = pointer.From(input.WindowsConfiguration.TimeZone) + output.AutomaticUpdateEnabled = pointer.From(input.WindowsConfiguration.EnableAutomaticUpdates) + output.ProvisionVmAgentEnabled = pointer.From(input.WindowsConfiguration.ProvisionVMAgent) + output.ProvisionVmConfigAgentEnabled = pointer.From(input.WindowsConfiguration.ProvisionVMConfigAgent) + output.SshPublicKey = flattenVirtualMachineOsProfileSsh(input.WindowsConfiguration.Ssh) + output.TimeZone = pointer.From(input.WindowsConfiguration.TimeZone) } return []StackHCIVirtualMachineOsProfileWindows{ - result, + output, } } @@ -818,40 +817,37 @@ func flattenVirtualMachineOsProfileSsh(input *virtualmachineinstances.SshConfigu return output } -func expandVirtualMachineSecurityProfile(input []StackHCIVirtualMachineSecurityProfile) *virtualmachineinstances.VirtualMachineInstancePropertiesSecurityProfile { - if len(input) == 0 { - return nil - } - - v := input[0] +func expandVirtualMachineSecurityProfile(input StackHCIWindowsVirtualMachineResourceModel) *virtualmachineinstances.VirtualMachineInstancePropertiesSecurityProfile { output := &virtualmachineinstances.VirtualMachineInstancePropertiesSecurityProfile{ - EnableTPM: pointer.To(v.TpmEnabled), - SecurityType: pointer.To(virtualmachineinstances.SecurityTypes(v.SecurityType)), + EnableTPM: pointer.To(input.TpmEnabled), + SecurityType: pointer.To(virtualmachineinstances.SecurityTypes(input.SecurityType)), UefiSettings: &virtualmachineinstances.VirtualMachineInstancePropertiesSecurityProfileUefiSettings{ - SecureBootEnabled: pointer.To(v.SecureBootEnabled), + SecureBootEnabled: pointer.To(input.SecureBootEnabled), }, } return output } -func flattenVirtualMachineSecurityProfile(input *virtualmachineinstances.VirtualMachineInstancePropertiesSecurityProfile) []StackHCIVirtualMachineSecurityProfile { - if input == nil { - return make([]StackHCIVirtualMachineSecurityProfile, 0) +func expandVirtualMachineStorageProfileWindowsForUpdate(input []StackHCIVirtualMachineStorageProfile) *virtualmachineinstances.StorageProfileUpdate { + if len(input) == 0 { + return nil } - secureBootEnabled := false - if input.UefiSettings != nil { - secureBootEnabled = pointer.From(input.UefiSettings.SecureBootEnabled) + v := input[0] + + dataDiskIds := make([]virtualmachineinstances.StorageProfileUpdateDataDisksInlined, 0) + for _, dataDiskId := range v.DataDiskIds { + dataDiskIds = append(dataDiskIds, virtualmachineinstances.StorageProfileUpdateDataDisksInlined{ + Id: pointer.To(dataDiskId), + }) } - return []StackHCIVirtualMachineSecurityProfile{ - { - TpmEnabled: pointer.From(input.EnableTPM), - SecurityType: string(pointer.From(input.SecurityType)), - SecureBootEnabled: secureBootEnabled, - }, + output := &virtualmachineinstances.StorageProfileUpdate{ + DataDisks: pointer.To(dataDiskIds), } + + return output } func expandVirtualMachineStorageProfileWindows(input []StackHCIVirtualMachineStorageProfile) *virtualmachineinstances.VirtualMachineInstancePropertiesStorageProfile { diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go index 2b0abd9d2ea1..da6bdfc630b6 100644 --- a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go @@ -33,7 +33,7 @@ func TestAccStackHCIWindowsVirtualMachine_basic(t *testing.T) { check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep(), + data.ImportStep("os_profile.0.admin_password"), }) } @@ -52,7 +52,7 @@ func TestAccStackHCIWindowsVirtualMachine_complete(t *testing.T) { check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep(), + data.ImportStep("os_profile.0.admin_password", "http_proxy_configuration.0.http_proxy", "http_proxy_configuration.0.https_proxy"), }) } @@ -71,28 +71,21 @@ func TestAccStackHCIWindowsVirtualMachine_update(t *testing.T) { check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep(), + data.ImportStep("os_profile.0.admin_password"), { Config: r.update(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep(), - { - Config: r.updateTag(data), - Check: acceptance.ComposeTestCheckFunc( - check.That(data.ResourceName).ExistsInAzure(r), - ), - }, - data.ImportStep(), + data.ImportStep("os_profile.0.admin_password", "http_proxy_configuration.0.http_proxy", "http_proxy_configuration.0.https_proxy"), { Config: r.basic(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), }, - data.ImportStep(), + data.ImportStep("os_profile.0.admin_password"), }) } @@ -142,13 +135,13 @@ provider "azurerm" { } resource "azurerm_stack_hci_windows_virtual_machine" "test" { - arc_machine_id = azurerm_arc_machine.test.id + arc_machine_id = azurerm_arc_machine.test.id custom_location_id = %[3]q hardware_profile { - vm_size = "Custom" + vm_size = "Custom" processor_number = 2 - memory_mb = 8192 + memory_mb = 8192 } network_profile { @@ -158,72 +151,98 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { os_profile { admin_username = "adminuser" admin_password = "!password!@#$" - computer_name = "1a" + computer_name = "testvm" } storage_profile { data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id] - image_id = azurerm_stack_hci_marketplace_gallery_image.test.id + //image_id = azurerm_stack_hci_marketplace_gallery_image.test.id } depends_on = [azurerm_role_assignment.test] + + lifecycle { + ignore_changes = [storage_profile.0.vm_config_storage_path_id] + } } -`, template, data.RandomString, os.Getenv(customLocationIdEnv)) +`, template, data.RandomInteger, os.Getenv(customLocationIdEnv)) } func (r StackHCIWindowsVirtualMachineResource) update(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` -%s +%[1]s provider "azurerm" { features {} } -resource "azurerm_stack_hci_virtual_hard_disk" "test" { - name = "acctest-vhd-%s" +resource "tls_private_key" "rsa-4096-example" { + algorithm = "RSA" + rsa_bits = 4096 +} + +resource "azurerm_stack_hci_virtual_hard_disk" "test2" { + name = "acctest-vhd2-%[2]d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location - custom_location_id = %q + custom_location_id = %[3]q disk_size_in_gb = 2 - tags = { - foo = "bar" - } - lifecycle { ignore_changes = [storage_path_id] + create_before_destroy = true } } -`, template, data.RandomString, os.Getenv(customLocationIdEnv)) -} -func (r StackHCIWindowsVirtualMachineResource) updateTag(data acceptance.TestData) string { - template := r.template(data) - return fmt.Sprintf(` -%s +resource "azurerm_stack_hci_network_interface" "test2" { + name = "acctest-ni2-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = %[3]q -provider "azurerm" { - features {} + ip_configuration { + subnet_id = azurerm_stack_hci_logical_network.test.id + } + + lifecycle { + ignore_changes = [mac_address, ip_configuration.0.private_ip_address] + create_before_destroy = true + } } -resource "azurerm_stack_hci_virtual_hard_disk" "test" { - name = "acctest-vhd-%s" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - custom_location_id = %q - disk_size_in_gb = 2 +resource "azurerm_stack_hci_windows_virtual_machine" "test" { + arc_machine_id = azurerm_arc_machine.test.id + custom_location_id = %[3]q + + hardware_profile { + vm_size = "Custom" + processor_number = 2 + memory_mb = 8192 + } - tags = { - env = "test" - foo = "bar" + network_profile { + network_interface_ids = [azurerm_stack_hci_network_interface.test.id, azurerm_stack_hci_network_interface.test2.id] } + os_profile { + admin_username = "adminuser" + admin_password = "!password!@#$" + computer_name = "testvm" + } + + storage_profile { + data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id, azurerm_stack_hci_virtual_hard_disk.test2.id] + //image_id = azurerm_stack_hci_marketplace_gallery_image.test.id + } + + depends_on = [azurerm_role_assignment.test] + lifecycle { - ignore_changes = [storage_path_id] + ignore_changes = [storage_profile.0.vm_config_storage_path_id] } } -`, template, data.RandomString, os.Getenv(customLocationIdEnv)) +`, template, data.RandomInteger, os.Getenv(customLocationIdEnv)) } func (r StackHCIWindowsVirtualMachineResource) complete(data acceptance.TestData) string { @@ -235,34 +254,103 @@ provider "azurerm" { features {} } +resource "tls_private_key" "rsa-4096-example" { + algorithm = "RSA" + rsa_bits = 4096 +} + resource "azurerm_stack_hci_storage_path" "test" { - name = "acctest-sp-%[2]s" + name = "acctest-sp-%[2]d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location custom_location_id = %[3]q - path = "C:\\ClusterStorage\\UserStorage_2\\sp-%[2]s" + path = "C:\\ClusterStorage\\UserStorage_2\\sp2-%[4]s" } -resource "azurerm_stack_hci_virtual_hard_disk" "test" { - name = "acctest-vhd-%[2]s" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - custom_location_id = %[3]q - disk_size_in_gb = 2 - dynamic_enabled = false - hyperv_generation = "V2" - physical_sector_in_bytes = 4096 - logical_sector_in_bytes = 512 - block_size_in_bytes = 1024 - disk_file_format = "vhdx" - storage_path_id = azurerm_stack_hci_storage_path.test.id - - tags = { - foo = "bar" - env = "test" +resource "azurerm_stack_hci_virtual_hard_disk" "test2" { + name = "acctest-vhd2-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = %[3]q + disk_size_in_gb = 2 + + lifecycle { + ignore_changes = [storage_path_id] } } -`, template, data.RandomString, os.Getenv(customLocationIdEnv)) + +resource "azurerm_stack_hci_network_interface" "test2" { + name = "acctest-ni2-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = %[3]q + dns_servers = ["192.168.1.254"] + + ip_configuration { + private_ip_address = "192.168.1.18" + subnet_id = azurerm_stack_hci_logical_network.test.id + } + + lifecycle { + ignore_changes = [mac_address] + } +} + +resource "azurerm_stack_hci_windows_virtual_machine" "test" { + arc_machine_id = azurerm_arc_machine.test.id + custom_location_id = %[3]q + tpm_enabled = true + secure_boot_enabled = true + + hardware_profile { + vm_size = "Custom" + processor_number = 2 + memory_mb = 8192 + dynamic_memory { + maximum_memory_mb = 8192 + minimum_memory_mb = 512 + target_memory_buffer = 20 + } + } + + network_profile { + network_interface_ids = [azurerm_stack_hci_network_interface.test.id, azurerm_stack_hci_network_interface.test2.id] + } + + os_profile { + admin_username = "adminuser" + admin_password = "!password!@#$" + computer_name = "testvm2" + automatic_update_enabled = true + time_zone = "UTC" + provision_vm_agent_enabled = true + provision_vm_config_agent_enabled = true + ssh_public_key { + path = "C:\\Users\\adminuser\\.ssh\\rsa.pub" + key_data = tls_private_key.rsa-4096-example.public_key_openssh + } + } + + storage_profile { + data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id] + //image_id = azurerm_stack_hci_marketplace_gallery_image.test.id + vm_config_storage_path_id = azurerm_stack_hci_storage_path.test.id + } + + http_proxy_configuration { + http_proxy = "http://proxy.example.com:3128" + https_proxy = "http://proxy.example.com:3128" + no_proxy = [ + "localhost", + "127.0.0.1", + "mcr.microsoft.com" + ] + trusted_ca = "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJ6RENDQVMyZ0F3SUJBZ0lCQVRBS0JnZ3Foa2pPUFFRREJEQVFNUTR3REFZRFZRUUtFd1ZGVGtOUFRUQWUKRncwd09URXhNVEF5TXpBd01EQmFGdzB4TURBMU1Ea3lNekF3TURCYU1CQXhEakFNQmdOVkJBb1RCVVZPUTA5TgpNSUdiTUJBR0J5cUdTTTQ5QWdFR0JTdUJCQUFqQTRHR0FBUUJBcUN1Um94NU4zTVRVOHdUdUllSUJYRjdpTW5oCm50cW1HVktRMGhmUUZEUUd2K0x5ZHVvN0pQcUZwL1kyamxYU2ROckFkejVXeGJyWStrRHhJcGtCUXRJQWtJREQKWlZtVHVlcTNaREFmY0dkRU5uek5KVkNhUGxIWEpMdkVFSU5jb0prVU8rK2NWeXl3ZHJlVkpjNjd2aE54MVRkWApWM3BwN2YrUmJPbU5LYm5WUkJ5ak5UQXpNQTRHQTFVZER3RUIvd1FFQXdJSGdEQVRCZ05WSFNVRUREQUtCZ2dyCkJnRUZCUWNEQVRBTUJnTlZIUk1CQWY4RUFqQUFNQW9HQ0NxR1NNNDlCQU1FQTRHTUFEQ0JpQUpDQWJiYjdzdkkKNXR1aEN5QTNqUVRTZ0E4enB2azBZV05Ya1owN3h6ZFY4amRNTXVtQ2FXOXljRUlxSjVLU3F1dVBoVXc5b2VregpCNTFkYXliVjFWUVhWVmRWQWtJQStrTU1TSnp3dHpIcU5BVVRtaVpQY2c3SDh2MUFTbDR0UjZscEtUcFVQWTJYCmxYT0N0MllmNGRzRnNpanV2emJKQmR4NzVkNEVmNVRSSFBjZytQSE5aZ2c9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K" + } + + depends_on = [azurerm_role_assignment.test] +} +`, template, data.RandomInteger, os.Getenv(customLocationIdEnv), data.RandomString) } func (r StackHCIWindowsVirtualMachineResource) requiresImport(data acceptance.TestData) string { @@ -284,34 +372,44 @@ resource "azurerm_stack_hci_virtual_hard_disk" "import" { func (r StackHCIWindowsVirtualMachineResource) template(data acceptance.TestData) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { - name = "acctest-hci-vm-%[2]s" + name = "acctest-hci-vm-%[2]d" location = %[1]q } resource "azurerm_stack_hci_logical_network" "test" { - name = "acctest-ln-%[2]s" + name = "acctest-ln-%[2]d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location custom_location_id = %[3]q virtual_switch_name = "ConvergedSwitch(managementcompute)" + dns_servers = ["192.168.1.254"] subnet { - ip_allocation_method = "Dynamic" + ip_allocation_method = "Static" + address_prefix = "192.168.1.0/24" + ip_pool { + start = "192.168.1.0" + end = "192.168.1.255" + } + + route { + address_prefix = "0.0.0.0/0" + next_hop_ip_address = "192.168.1.1" + } } } resource "azurerm_stack_hci_network_interface" "test" { - name = "acctest-ni-%[2]s" + name = "acctest-ni-%[2]d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location custom_location_id = %[3]q + dns_servers = ["192.168.1.254"] + mac_address = "02:ec:01:0c:00:08" ip_configuration { - subnet_id = azurerm_stack_hci_logical_network.test.id - } - - lifecycle { - ignore_changes = [mac_address] + private_ip_address = "192.168.1.15" + subnet_id = azurerm_stack_hci_logical_network.test.id } } @@ -327,7 +425,7 @@ resource "azurerm_role_assignment" "test" { } //resource "azurerm_stack_hci_marketplace_gallery_image" "test" { -// name = "acctest-mgi-%[2]s" +// name = "acctest-mgi-%[2]d" // resource_group_name = azurerm_resource_group.test.name // location = azurerm_resource_group.test.location // custom_location_id = %[3]q @@ -344,31 +442,26 @@ resource "azurerm_role_assignment" "test" { //} resource "azurerm_stack_hci_virtual_hard_disk" "test" { - name = "acctest-vhd-%[2]s" + name = "acctest-vhd-%[2]d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location custom_location_id = %[3]q disk_size_in_gb = 2 - tags = { - env = "test" - foo = "bar" - } - lifecycle { ignore_changes = [storage_path_id] } } resource "azurerm_arc_machine" "test" { - name = "acctest-hcivm-%[2]s" + name = "acctest-hcivm-%[2]d" resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location kind = "HCI" - //identity { - // type = "SystemAssigned" - //} + identity { + type = "SystemAssigned" + } } -`, data.Locations.Primary, data.RandomString, os.Getenv(customLocationIdEnv)) +`, data.Locations.Primary, data.RandomInteger, os.Getenv(customLocationIdEnv)) } From c3685fc84f88dc181e84d55e79032cfc6d765315 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Fri, 13 Dec 2024 07:21:56 +0000 Subject: [PATCH 05/16] fix test --- ..._hci_marketplace_gallery_image_resource.go | 44 ++++++ .../stack_hci_network_interface_resource.go | 44 ++++++ .../stack_hci_virtual_hard_disk_resource.go | 44 ++++++ ...ck_hci_windows_virtual_machine_resource.go | 47 +++++++ ...i_windows_virtual_machine_resource_test.go | 127 ++++++++++++------ 5 files changed, 263 insertions(+), 43 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource.go b/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource.go index 517cc7ceb217..8c1f102cf60c 100644 --- a/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource.go +++ b/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource.go @@ -6,6 +6,7 @@ package azurestackhci import ( "context" "fmt" + "log" "regexp" "time" @@ -193,6 +194,10 @@ func (r StackHCIMarketplaceGalleryImageResource) Create() sdk.ResourceFunc { return fmt.Errorf("performing create %s: %+v", id, err) } + if err := resourceMarketplaceGalleryImageWaitForCreated(ctx, *client, id); err != nil { + return fmt.Errorf("waiting for %s to be created: %+v", id, err) + } + metadata.SetID(id) return nil @@ -330,3 +335,42 @@ func flattenStackHCIMarketplaceGalleryImageIdentifier(input *marketplacegalleryi }, } } + +func resourceMarketplaceGalleryImageWaitForCreated(ctx context.Context, client marketplacegalleryimages.MarketplaceGalleryImagesClient, id marketplacegalleryimages.MarketplaceGalleryImageId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("internal error: context had no deadline") + } + + state := &pluginsdk.StateChangeConf{ + MinTimeout: 10 * time.Second, + ContinuousTargetOccurence: 2, + Pending: []string{"NotFound"}, + Target: []string{"Found"}, + Refresh: resourceMarketplaceGalleryImageRefreshFunc(ctx, client, id), + Timeout: time.Until(deadline), + } + + if _, err := state.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for %s to be created: %+v", id, err) + } + + return nil +} + +func resourceMarketplaceGalleryImageRefreshFunc(ctx context.Context, client marketplacegalleryimages.MarketplaceGalleryImagesClient, id marketplacegalleryimages.MarketplaceGalleryImageId) pluginsdk.StateRefreshFunc { + return func() (interface{}, string, error) { + log.Printf("[DEBUG] Checking status for %s ..", id) + + resp, err := client.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return resp, "NotFound", nil + } + + return resp, "Error", fmt.Errorf("retrieving %s: %+v", id, err) + } + + return resp, "Found", nil + } +} diff --git a/internal/services/azurestackhci/stack_hci_network_interface_resource.go b/internal/services/azurestackhci/stack_hci_network_interface_resource.go index 35ef99c81535..8f02f2b598a6 100644 --- a/internal/services/azurestackhci/stack_hci_network_interface_resource.go +++ b/internal/services/azurestackhci/stack_hci_network_interface_resource.go @@ -6,6 +6,7 @@ package azurestackhci import ( "context" "fmt" + "log" "regexp" "time" @@ -181,6 +182,10 @@ func (r StackHCINetworkInterfaceResource) Create() sdk.ResourceFunc { return fmt.Errorf("performing create %s: %+v", id, err) } + if err := resourceNetworkInterfaceWaitForCreated(ctx, *client, id); err != nil { + return err + } + metadata.SetID(id) return nil @@ -352,3 +357,42 @@ func flattenStackHCINetworkInterfaceIPConfiguration(input *[]networkinterfaces.I return results, nil } + +func resourceNetworkInterfaceWaitForCreated(ctx context.Context, client networkinterfaces.NetworkInterfacesClient, id networkinterfaces.NetworkInterfaceId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("internal error: context had no deadline") + } + + state := &pluginsdk.StateChangeConf{ + MinTimeout: 10 * time.Second, + ContinuousTargetOccurence: 2, + Pending: []string{"NotFound"}, + Target: []string{"Found"}, + Refresh: resourceNetworkInterfaceRefreshFunc(ctx, client, id), + Timeout: time.Until(deadline), + } + + if _, err := state.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for %s to be created: %+v", id, err) + } + + return nil +} + +func resourceNetworkInterfaceRefreshFunc(ctx context.Context, client networkinterfaces.NetworkInterfacesClient, id networkinterfaces.NetworkInterfaceId) pluginsdk.StateRefreshFunc { + return func() (interface{}, string, error) { + log.Printf("[DEBUG] Checking status for %s ..", id) + + resp, err := client.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return resp, "NotFound", nil + } + + return resp, "Error", fmt.Errorf("retrieving %s: %+v", id, err) + } + + return resp, "Found", nil + } +} diff --git a/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go b/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go index 0c8f022d3050..373a551586b9 100644 --- a/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go +++ b/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go @@ -6,6 +6,7 @@ package azurestackhci import ( "context" "fmt" + "log" "regexp" "time" @@ -209,6 +210,10 @@ func (r StackHCIVirtualHardDiskResource) Create() sdk.ResourceFunc { return fmt.Errorf("performing create %s: %+v", id, err) } + if err := resourceVirtualHardDiskWaitForCreated(ctx, *client, id); err != nil { + return err + } + metadata.SetID(id) return nil @@ -319,3 +324,42 @@ func (r StackHCIVirtualHardDiskResource) Delete() sdk.ResourceFunc { }, } } + +func resourceVirtualHardDiskWaitForCreated(ctx context.Context, client virtualharddisks.VirtualHardDisksClient, id virtualharddisks.VirtualHardDiskId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("internal error: context had no deadline") + } + + state := &pluginsdk.StateChangeConf{ + MinTimeout: 10 * time.Second, + ContinuousTargetOccurence: 2, + Pending: []string{"NotFound"}, + Target: []string{"Found"}, + Refresh: resourceVirtualHardDiskRefreshFunc(ctx, client, id), + Timeout: time.Until(deadline), + } + + if _, err := state.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for %s to be created: %+v", id, err) + } + + return nil +} + +func resourceVirtualHardDiskRefreshFunc(ctx context.Context, client virtualharddisks.VirtualHardDisksClient, id virtualharddisks.VirtualHardDiskId) pluginsdk.StateRefreshFunc { + return func() (interface{}, string, error) { + log.Printf("[DEBUG] Checking status for %s ..", id) + + resp, err := client.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return resp, "NotFound", nil + } + + return resp, "Error", fmt.Errorf("retrieving %s: %+v", id, err) + } + + return resp, "Found", nil + } +} diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go index aabd605ad89d..3381a17ad3fa 100644 --- a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go @@ -3,6 +3,7 @@ package azurestackhci import ( "context" "fmt" + "log" "regexp" "time" @@ -438,6 +439,10 @@ func (r StackHCIWindowsVirtualMachineResource) Create() sdk.ResourceFunc { return fmt.Errorf("creating %s: %+v", id, err) } + if err := resourceVirtualMachineWaitForCreated(ctx, *client, id); err != nil { + return err + } + metadata.SetID(id) return nil @@ -918,3 +923,45 @@ func flattenVirtualMachineStorageProfileWindows(input *virtualmachineinstances.V }, } } + +func resourceVirtualMachineWaitForCreated(ctx context.Context, client virtualmachineinstances.VirtualMachineInstancesClient, id parse.StackHCIVirtualMachineId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("internal error: context had no deadline") + } + + state := &pluginsdk.StateChangeConf{ + MinTimeout: 10 * time.Second, + ContinuousTargetOccurence: 2, + Pending: []string{"NotFound"}, + Target: []string{"Found"}, + Refresh: resourceVirtualMachineRefreshFunc(ctx, client, id), + Timeout: time.Until(deadline), + } + + if _, err := state.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for %s to be created: %+v", id, err) + } + + return nil +} + +func resourceVirtualMachineRefreshFunc(ctx context.Context, client virtualmachineinstances.VirtualMachineInstancesClient, id parse.StackHCIVirtualMachineId) pluginsdk.StateRefreshFunc { + return func() (interface{}, string, error) { + log.Printf("[DEBUG] Checking status for %s ..", id) + + arcMachineId := machines.NewMachineID(id.SubscriptionId, id.ResourceGroup, id.MachineName) + scopeId := commonids.NewScopeID(arcMachineId.ID()) + + resp, err := client.Get(ctx, scopeId) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return resp, "NotFound", nil + } + + return resp, "Error", fmt.Errorf("retrieving %s: %+v", id, err) + } + + return resp, "Found", nil + } +} diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go index da6bdfc630b6..cfcf957246b0 100644 --- a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go @@ -18,7 +18,25 @@ import ( type StackHCIWindowsVirtualMachineResource struct{} -func TestAccStackHCIWindowsVirtualMachine_basic(t *testing.T) { +func TestAccStackHCIWindowsVirtualMachine(t *testing.T) { + // NOTE: this is a combined test rather than separate split out tests due to + // the test environment network limitation + // (which our test suite can't easily work around) + + testCases := map[string]func(t *testing.T){ + "basic": testAccStackHCIWindowsVirtualMachine_basic, + "complete": testAccStackHCIWindowsVirtualMachine_complete, + "update": testAccStackHCIWindowsVirtualMachine_update, + "requiresImport": testAccStackHCIWindowsVirtualMachine_requiresImport, + } + for name, m := range testCases { + t.Run(name, func(t *testing.T) { + m(t) + }) + } +} + +func testAccStackHCIWindowsVirtualMachine_basic(t *testing.T) { if os.Getenv(customLocationIdEnv) == "" { t.Skipf("skipping since %q has not been specified", customLocationIdEnv) } @@ -26,7 +44,7 @@ func TestAccStackHCIWindowsVirtualMachine_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_stack_hci_windows_virtual_machine", "test") r := StackHCIWindowsVirtualMachineResource{} - data.ResourceTest(t, r, []acceptance.TestStep{ + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), Check: acceptance.ComposeTestCheckFunc( @@ -37,7 +55,7 @@ func TestAccStackHCIWindowsVirtualMachine_basic(t *testing.T) { }) } -func TestAccStackHCIWindowsVirtualMachine_complete(t *testing.T) { +func testAccStackHCIWindowsVirtualMachine_complete(t *testing.T) { if os.Getenv(customLocationIdEnv) == "" { t.Skipf("skipping since %q has not been specified", customLocationIdEnv) } @@ -45,7 +63,7 @@ func TestAccStackHCIWindowsVirtualMachine_complete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_stack_hci_windows_virtual_machine", "test") r := StackHCIWindowsVirtualMachineResource{} - data.ResourceTest(t, r, []acceptance.TestStep{ + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ { Config: r.complete(data), Check: acceptance.ComposeTestCheckFunc( @@ -56,7 +74,7 @@ func TestAccStackHCIWindowsVirtualMachine_complete(t *testing.T) { }) } -func TestAccStackHCIWindowsVirtualMachine_update(t *testing.T) { +func testAccStackHCIWindowsVirtualMachine_update(t *testing.T) { if os.Getenv(customLocationIdEnv) == "" { t.Skipf("skipping since %q has not been specified", customLocationIdEnv) } @@ -64,7 +82,7 @@ func TestAccStackHCIWindowsVirtualMachine_update(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_stack_hci_windows_virtual_machine", "test") r := StackHCIWindowsVirtualMachineResource{} - data.ResourceTest(t, r, []acceptance.TestStep{ + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), Check: acceptance.ComposeTestCheckFunc( @@ -89,7 +107,7 @@ func TestAccStackHCIWindowsVirtualMachine_update(t *testing.T) { }) } -func TestAccStackHCIWindowsVirtualMachine_requiresImport(t *testing.T) { +func testAccStackHCIWindowsVirtualMachine_requiresImport(t *testing.T) { if os.Getenv(customLocationIdEnv) == "" { t.Skipf("skipping since %q has not been specified", customLocationIdEnv) } @@ -97,7 +115,7 @@ func TestAccStackHCIWindowsVirtualMachine_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_stack_hci_windows_virtual_machine", "test") r := StackHCIWindowsVirtualMachineResource{} - data.ResourceTest(t, r, []acceptance.TestStep{ + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ { Config: r.basic(data), Check: acceptance.ComposeTestCheckFunc( @@ -156,7 +174,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { storage_profile { data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id] - //image_id = azurerm_stack_hci_marketplace_gallery_image.test.id + image_id = azurerm_stack_hci_marketplace_gallery_image.test.id } depends_on = [azurerm_role_assignment.test] @@ -168,6 +186,45 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { `, template, data.RandomInteger, os.Getenv(customLocationIdEnv)) } +func (r StackHCIWindowsVirtualMachineResource) requiresImport(data acceptance.TestData) string { + config := r.basic(data) + + return fmt.Sprintf(` +%s + +resource "azurerm_stack_hci_windows_virtual_machine" "import" { + arc_machine_id = azurerm_stack_hci_windows_virtual_machine.test.arc_machine_id + custom_location_id = %[3]q + + hardware_profile { + vm_size = "Custom" + processor_number = 2 + memory_mb = 8192 + } + + network_profile { + network_interface_ids = [azurerm_stack_hci_network_interface.test.id] + } + + os_profile { + admin_username = "adminuser" + computer_name = "testvm" + } + + storage_profile { + data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id] + image_id = azurerm_stack_hci_marketplace_gallery_image.test.id + } + + depends_on = [azurerm_role_assignment.test] + + lifecycle { + ignore_changes = [storage_profile.0.vm_config_storage_path_id] + } +} +`, config, data.RandomInteger, os.Getenv(customLocationIdEnv)) +} + func (r StackHCIWindowsVirtualMachineResource) update(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` @@ -233,7 +290,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { storage_profile { data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id, azurerm_stack_hci_virtual_hard_disk.test2.id] - //image_id = azurerm_stack_hci_marketplace_gallery_image.test.id + image_id = azurerm_stack_hci_marketplace_gallery_image.test.id } depends_on = [azurerm_role_assignment.test] @@ -333,7 +390,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { storage_profile { data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id] - //image_id = azurerm_stack_hci_marketplace_gallery_image.test.id + image_id = azurerm_stack_hci_marketplace_gallery_image.test.id vm_config_storage_path_id = azurerm_stack_hci_storage_path.test.id } @@ -353,22 +410,6 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { `, template, data.RandomInteger, os.Getenv(customLocationIdEnv), data.RandomString) } -func (r StackHCIWindowsVirtualMachineResource) requiresImport(data acceptance.TestData) string { - config := r.basic(data) - - return fmt.Sprintf(` -%s - -resource "azurerm_stack_hci_virtual_hard_disk" "import" { - name = azurerm_stack_hci_virtual_hard_disk.test.name - resource_group_name = azurerm_stack_hci_virtual_hard_disk.test.resource_group_name - location = azurerm_stack_hci_virtual_hard_disk.test.location - custom_location_id = azurerm_stack_hci_virtual_hard_disk.test.custom_location_id - disk_size_in_gb = azurerm_stack_hci_virtual_hard_disk.test.disk_size_in_gb -} -`, config) -} - func (r StackHCIWindowsVirtualMachineResource) template(data acceptance.TestData) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { @@ -424,22 +465,22 @@ resource "azurerm_role_assignment" "test" { principal_id = data.azuread_service_principal.hciRp.object_id } -//resource "azurerm_stack_hci_marketplace_gallery_image" "test" { -// name = "acctest-mgi-%[2]d" -// resource_group_name = azurerm_resource_group.test.name -// location = azurerm_resource_group.test.location -// custom_location_id = %[3]q -// hyperv_generation = "V2" -// os_type = "Windows" -// version = "20348.2655.240905" -// identifier { -// publisher = "MicrosoftWindowsServer" -// offer = "WindowsServer" -// sku = "2022-datacenter-azure-edition-core" -// } -// -// depends_on = [azurerm_role_assignment.test] -//} +resource "azurerm_stack_hci_marketplace_gallery_image" "test" { + name = "acctest-mgi-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = %[3]q + hyperv_generation = "V2" + os_type = "Windows" + version = "20348.2762.241204" + identifier { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2022-datacenter-azure-edition-core" + } + + depends_on = [azurerm_role_assignment.test] +} resource "azurerm_stack_hci_virtual_hard_disk" "test" { name = "acctest-vhd-%[2]d" From 2c04b3206580ece93b44d5924578afb25d1f2fea Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Fri, 13 Dec 2024 08:58:03 +0000 Subject: [PATCH 06/16] fix --- .../stack_hci_marketplace_gallery_image_resource_test.go | 8 ++++++-- .../stack_hci_windows_virtual_machine_resource_test.go | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource_test.go b/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource_test.go index 4776b7ee70b3..1cd11ab13402 100644 --- a/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource_test.go @@ -224,6 +224,8 @@ resource "azurerm_stack_hci_marketplace_gallery_image" "test" { offer = "WindowsServer" sku = "2022-datacenter-azure-edition-core" } + + depends_on = [azurerm_role_assignment.test] } `, template, data.RandomString, os.Getenv(customLocationIdEnv), r.imageVersion) } @@ -262,6 +264,8 @@ resource "azurerm_stack_hci_marketplace_gallery_image" "test" { tags = { foo = "bar" } + + depends_on = [azurerm_role_assignment.test] } `, template, data.RandomString, os.Getenv(customLocationIdEnv), r.imageVersion) } @@ -301,6 +305,8 @@ resource "azurerm_stack_hci_marketplace_gallery_image" "test" { foo = "bar" env = "test" } + + depends_on = [azurerm_role_assignment.test] } `, template, data.RandomString, os.Getenv(customLocationIdEnv), r.imageVersion) } @@ -312,8 +318,6 @@ resource "azurerm_resource_group" "test" { location = "%s" } -data "azurerm_client_config" "test" {} - // service principal of 'Microsoft.AzureStackHCI Resource Provider' data "azuread_service_principal" "hciRp" { client_id = "1412d89f-b8a8-4111-b4fd-e82905cbd85d" diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go index cfcf957246b0..6dedc405ba66 100644 --- a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go @@ -472,7 +472,7 @@ resource "azurerm_stack_hci_marketplace_gallery_image" "test" { custom_location_id = %[3]q hyperv_generation = "V2" os_type = "Windows" - version = "20348.2762.241204" + version = "20348.2655.240810" identifier { publisher = "MicrosoftWindowsServer" offer = "WindowsServer" From 680800c5a7511cfbd740ecaf3e3b1984ec961be3 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Fri, 13 Dec 2024 08:58:27 +0000 Subject: [PATCH 07/16] fix --- .../stack_hci_windows_virtual_machine_resource_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go index 6dedc405ba66..cfcf957246b0 100644 --- a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go @@ -472,7 +472,7 @@ resource "azurerm_stack_hci_marketplace_gallery_image" "test" { custom_location_id = %[3]q hyperv_generation = "V2" os_type = "Windows" - version = "20348.2655.240810" + version = "20348.2762.241204" identifier { publisher = "MicrosoftWindowsServer" offer = "WindowsServer" From 3c5068c8d01fbf1e37a1bd159d3b3a38044f72af Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Mon, 16 Dec 2024 09:23:50 +0000 Subject: [PATCH 08/16] wip --- ...ck_hci_windows_virtual_machine_resource.go | 12 +- ...i_windows_virtual_machine_resource_test.go | 43 +++-- ..._hci_windows_virtual_machine.html.markdown | 169 ++++++++++++++++++ 3 files changed, 203 insertions(+), 21 deletions(-) create mode 100644 website/docs/r/stack_hci_windows_virtual_machine.html.markdown diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go index 3381a17ad3fa..be08c4d58e60 100644 --- a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonids" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/galleryimages" "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/marketplacegalleryimages" "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/networkinterfaces" "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/storagecontainers" @@ -321,10 +322,13 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S }, "image_id": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: marketplacegalleryimages.ValidateMarketplaceGalleryImageID, + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.Any( + marketplacegalleryimages.ValidateMarketplaceGalleryImageID, + galleryimages.ValidateGalleryImageID, + ), }, "os_disk_id": { diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go index cfcf957246b0..30e6b5f4818e 100644 --- a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go @@ -16,6 +16,14 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) +// https://learn.microsoft.com/en-us/azure/azure-local/manage/virtual-machine-image-azure-marketplace?tabs=azurecli +// Provisioning a VM requires an image. We support downloading an image using the `azurerm_stack_hci_marketplace_gallery_image` resource; +// however, downloading an image takes more than one hour. +// To speed up testing, we reuse a pre-downloaded image. +const ( + vmImageIdEnv = "ARM_TEST_STACK_HCI_VM_IMAGE_ID" +) + type StackHCIWindowsVirtualMachineResource struct{} func TestAccStackHCIWindowsVirtualMachine(t *testing.T) { @@ -41,6 +49,10 @@ func testAccStackHCIWindowsVirtualMachine_basic(t *testing.T) { t.Skipf("skipping since %q has not been specified", customLocationIdEnv) } + if os.Getenv(vmImageIdEnv) == "" { + t.Skipf("skipping since %q has not been specified", vmImageIdEnv) + } + data := acceptance.BuildTestData(t, "azurerm_stack_hci_windows_virtual_machine", "test") r := StackHCIWindowsVirtualMachineResource{} @@ -60,6 +72,10 @@ func testAccStackHCIWindowsVirtualMachine_complete(t *testing.T) { t.Skipf("skipping since %q has not been specified", customLocationIdEnv) } + if os.Getenv(vmImageIdEnv) == "" { + t.Skipf("skipping since %q has not been specified", vmImageIdEnv) + } + data := acceptance.BuildTestData(t, "azurerm_stack_hci_windows_virtual_machine", "test") r := StackHCIWindowsVirtualMachineResource{} @@ -79,6 +95,10 @@ func testAccStackHCIWindowsVirtualMachine_update(t *testing.T) { t.Skipf("skipping since %q has not been specified", customLocationIdEnv) } + if os.Getenv(vmImageIdEnv) == "" { + t.Skipf("skipping since %q has not been specified", vmImageIdEnv) + } + data := acceptance.BuildTestData(t, "azurerm_stack_hci_windows_virtual_machine", "test") r := StackHCIWindowsVirtualMachineResource{} @@ -112,6 +132,10 @@ func testAccStackHCIWindowsVirtualMachine_requiresImport(t *testing.T) { t.Skipf("skipping since %q has not been specified", customLocationIdEnv) } + if os.Getenv(vmImageIdEnv) == "" { + t.Skipf("skipping since %q has not been specified", vmImageIdEnv) + } + data := acceptance.BuildTestData(t, "azurerm_stack_hci_windows_virtual_machine", "test") r := StackHCIWindowsVirtualMachineResource{} @@ -465,23 +489,6 @@ resource "azurerm_role_assignment" "test" { principal_id = data.azuread_service_principal.hciRp.object_id } -resource "azurerm_stack_hci_marketplace_gallery_image" "test" { - name = "acctest-mgi-%[2]d" - resource_group_name = azurerm_resource_group.test.name - location = azurerm_resource_group.test.location - custom_location_id = %[3]q - hyperv_generation = "V2" - os_type = "Windows" - version = "20348.2762.241204" - identifier { - publisher = "MicrosoftWindowsServer" - offer = "WindowsServer" - sku = "2022-datacenter-azure-edition-core" - } - - depends_on = [azurerm_role_assignment.test] -} - resource "azurerm_stack_hci_virtual_hard_disk" "test" { name = "acctest-vhd-%[2]d" resource_group_name = azurerm_resource_group.test.name @@ -503,6 +510,8 @@ resource "azurerm_arc_machine" "test" { identity { type = "SystemAssigned" } + + depends_on = [azurerm_role_assignment.test] } `, data.Locations.Primary, data.RandomInteger, os.Getenv(customLocationIdEnv)) } diff --git a/website/docs/r/stack_hci_windows_virtual_machine.html.markdown b/website/docs/r/stack_hci_windows_virtual_machine.html.markdown new file mode 100644 index 000000000000..70730a2e33d1 --- /dev/null +++ b/website/docs/r/stack_hci_windows_virtual_machine.html.markdown @@ -0,0 +1,169 @@ +--- +subcategory: "Azure Stack HCI" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_stack_hci_windows_virtual_machine" +description: |- + Manages a Stack HCI Windows Virtual Machine. +--- + +# azurerm_stack_hci_windows_virtual_machine + +Manages a Stack HCI Windows Virtual Machine. + +## Example Usage + +```hcl +resource "azurerm_stack_hci_windows_virtual_machine" "example" { + + network_profile { + network_interface_ids = [ "example" ] + } + custom_location_id = "TODO" + arc_machine_id = "TODO" + + hardware_profile { + vm_size = "TODO" + processor_number = 42 + memory_mb = 42 + } + + os_profile { + admin_username = "TODO" + computer_name = "example" + } + + storage_profile { + data_disk_ids = [ "example" ] + image_id = "TODO" + } +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `arc_machine_id` - (Required) The ID of the TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `custom_location_id` - (Required) The ID of the TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `hardware_profile` - (Required) A `hardware_profile` block as defined below. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `network_profile` - (Required) A `network_profile` block as defined below. + +* `os_profile` - (Required) A `os_profile` block as defined below. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `storage_profile` - (Required) A `storage_profile` block as defined below. + +--- + +* `http_proxy_configuration` - (Optional) A `http_proxy_configuration` block as defined below. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `secure_boot_enabled` - (Optional) Should the TODO be enabled? Defaults to `true`. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `security_type` - (Optional) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `tpm_enabled` - (Optional) Should the TODO be enabled? Defaults to `false`. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +--- + +A `dynamic_memory` block supports the following: + +* `maximum_memory_mb` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `minimum_memory_mb` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `target_memory_buffer` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +--- + +A `hardware_profile` block supports the following: + +* `memory_mb` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `processor_number` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `vm_size` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `dynamic_memory` - (Optional) A `dynamic_memory` block as defined above. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +--- + +A `http_proxy_configuration` block supports the following: + +* `http_proxy` - (Optional) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `https_proxy` - (Optional) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `no_proxy` - (Optional) Specifies a list of TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `trusted_ca` - (Optional) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +--- + +A `network_profile` block supports the following: + +* `network_interface_ids` - (Required) Specifies a list of TODO. + +--- + +A `os_profile` block supports the following: + +* `admin_username` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `computer_name` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `admin_password` - (Optional) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `automatic_update_enabled` - (Optional) Should the TODO be enabled? Defaults to `false`. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `provision_vm_agent_enabled` - (Optional) Should the TODO be enabled? Defaults to `false`. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `provision_vm_config_agent_enabled` - (Optional) Should the TODO be enabled? Defaults to `false`. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `ssh_public_key` - (Optional) One or more `ssh_public_key` blocks as defined below. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `time_zone` - (Optional) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +--- + +A `ssh_public_key` block supports the following: + +* `key_data` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `path` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +--- + +A `storage_profile` block supports the following: + +* `data_disk_ids` - (Required) Specifies a list of TODO. + +* `image_id` - (Required) The ID of the TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `os_disk_id` - (Optional) The ID of the TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +* `vm_config_storage_path_id` - (Optional) The ID of the TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Stack HCI Windows Virtual Machine. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +* `create` - (Defaults to 1 hour and 30 minutes) Used when creating the Stack HCI Windows Virtual Machine. +* `read` - (Defaults to 5 minutes) Used when retrieving the Stack HCI Windows Virtual Machine. +* `update` - (Defaults to 1 hour and 30 minutes) Used when updating the Stack HCI Windows Virtual Machine. +* `delete` - (Defaults to 30 minutes) Used when deleting the Stack HCI Windows Virtual Machine. + +## Import + +Stack HCI Windows Virtual Machines can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_stack_hci_windows_virtual_machine.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.AzureStackHCI/virtualMachineInstances/default +``` \ No newline at end of file From 8021461750a9033155c613f2cdf0a71036d11805 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Tue, 17 Dec 2024 07:38:55 +0000 Subject: [PATCH 09/16] fix test --- ...k_hci_marketplace_gallery_image_resource.go | 2 +- .../stack_hci_network_interface_resource.go | 2 +- .../stack_hci_virtual_hard_disk_resource.go | 2 +- ...ack_hci_windows_virtual_machine_resource.go | 2 +- ...ci_windows_virtual_machine_resource_test.go | 18 ++++++++---------- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource.go b/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource.go index 8c1f102cf60c..a927b8ac4c13 100644 --- a/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource.go +++ b/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource.go @@ -344,7 +344,7 @@ func resourceMarketplaceGalleryImageWaitForCreated(ctx context.Context, client m state := &pluginsdk.StateChangeConf{ MinTimeout: 10 * time.Second, - ContinuousTargetOccurence: 2, + ContinuousTargetOccurence: 3, Pending: []string{"NotFound"}, Target: []string{"Found"}, Refresh: resourceMarketplaceGalleryImageRefreshFunc(ctx, client, id), diff --git a/internal/services/azurestackhci/stack_hci_network_interface_resource.go b/internal/services/azurestackhci/stack_hci_network_interface_resource.go index 8f02f2b598a6..5b8ff469e4c0 100644 --- a/internal/services/azurestackhci/stack_hci_network_interface_resource.go +++ b/internal/services/azurestackhci/stack_hci_network_interface_resource.go @@ -366,7 +366,7 @@ func resourceNetworkInterfaceWaitForCreated(ctx context.Context, client networki state := &pluginsdk.StateChangeConf{ MinTimeout: 10 * time.Second, - ContinuousTargetOccurence: 2, + ContinuousTargetOccurence: 3, Pending: []string{"NotFound"}, Target: []string{"Found"}, Refresh: resourceNetworkInterfaceRefreshFunc(ctx, client, id), diff --git a/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go b/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go index 373a551586b9..c2f5aa78a07c 100644 --- a/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go +++ b/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go @@ -333,7 +333,7 @@ func resourceVirtualHardDiskWaitForCreated(ctx context.Context, client virtualha state := &pluginsdk.StateChangeConf{ MinTimeout: 10 * time.Second, - ContinuousTargetOccurence: 2, + ContinuousTargetOccurence: 3, Pending: []string{"NotFound"}, Target: []string{"Found"}, Refresh: resourceVirtualHardDiskRefreshFunc(ctx, client, id), diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go index be08c4d58e60..dca75a507a73 100644 --- a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go @@ -936,7 +936,7 @@ func resourceVirtualMachineWaitForCreated(ctx context.Context, client virtualmac state := &pluginsdk.StateChangeConf{ MinTimeout: 10 * time.Second, - ContinuousTargetOccurence: 2, + ContinuousTargetOccurence: 3, Pending: []string{"NotFound"}, Target: []string{"Found"}, Refresh: resourceVirtualMachineRefreshFunc(ctx, client, id), diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go index 30e6b5f4818e..027a49fb7f41 100644 --- a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go @@ -198,7 +198,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { storage_profile { data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id] - image_id = azurerm_stack_hci_marketplace_gallery_image.test.id + image_id = %[4]q } depends_on = [azurerm_role_assignment.test] @@ -207,7 +207,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { ignore_changes = [storage_profile.0.vm_config_storage_path_id] } } -`, template, data.RandomInteger, os.Getenv(customLocationIdEnv)) +`, template, data.RandomInteger, os.Getenv(customLocationIdEnv), os.Getenv(vmImageIdEnv)) } func (r StackHCIWindowsVirtualMachineResource) requiresImport(data acceptance.TestData) string { @@ -237,7 +237,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "import" { storage_profile { data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id] - image_id = azurerm_stack_hci_marketplace_gallery_image.test.id + image_id = %[4]q } depends_on = [azurerm_role_assignment.test] @@ -246,7 +246,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "import" { ignore_changes = [storage_profile.0.vm_config_storage_path_id] } } -`, config, data.RandomInteger, os.Getenv(customLocationIdEnv)) +`, config, data.RandomInteger, os.Getenv(customLocationIdEnv), os.Getenv(vmImageIdEnv)) } func (r StackHCIWindowsVirtualMachineResource) update(data acceptance.TestData) string { @@ -314,7 +314,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { storage_profile { data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id, azurerm_stack_hci_virtual_hard_disk.test2.id] - image_id = azurerm_stack_hci_marketplace_gallery_image.test.id + image_id = %[4]q } depends_on = [azurerm_role_assignment.test] @@ -323,7 +323,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { ignore_changes = [storage_profile.0.vm_config_storage_path_id] } } -`, template, data.RandomInteger, os.Getenv(customLocationIdEnv)) +`, template, data.RandomInteger, os.Getenv(customLocationIdEnv), os.Getenv(vmImageIdEnv)) } func (r StackHCIWindowsVirtualMachineResource) complete(data acceptance.TestData) string { @@ -414,7 +414,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { storage_profile { data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id] - image_id = azurerm_stack_hci_marketplace_gallery_image.test.id + image_id = %[5]q vm_config_storage_path_id = azurerm_stack_hci_storage_path.test.id } @@ -431,7 +431,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { depends_on = [azurerm_role_assignment.test] } -`, template, data.RandomInteger, os.Getenv(customLocationIdEnv), data.RandomString) +`, template, data.RandomInteger, os.Getenv(customLocationIdEnv), data.RandomString, os.Getenv(vmImageIdEnv)) } func (r StackHCIWindowsVirtualMachineResource) template(data acceptance.TestData) string { @@ -510,8 +510,6 @@ resource "azurerm_arc_machine" "test" { identity { type = "SystemAssigned" } - - depends_on = [azurerm_role_assignment.test] } `, data.Locations.Primary, data.RandomInteger, os.Getenv(customLocationIdEnv)) } From 14277c844b185ebea6344b358b7855c6c5c88412 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Wed, 18 Dec 2024 07:58:21 +0000 Subject: [PATCH 10/16] fix test --- ...marketplace_gallery_image_resource_test.go | 8 + .../stack_hci_network_interface_resource.go | 2 +- ...ack_hci_network_interface_resource_test.go | 6 +- .../stack_hci_virtual_hard_disk_resource.go | 2 +- ...ck_hci_windows_virtual_machine_resource.go | 2 +- ...ci_marketplace_gallery_image.html.markdown | 3 + .../stack_hci_network_interface.html.markdown | 2 + .../stack_hci_virtual_hard_disk.html.markdown | 2 +- ..._hci_windows_virtual_machine.html.markdown | 216 +++++++++++++----- 9 files changed, 182 insertions(+), 61 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource_test.go b/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource_test.go index 1cd11ab13402..00aed136f837 100644 --- a/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource_test.go @@ -165,6 +165,10 @@ resource "azurerm_stack_hci_marketplace_gallery_image" "test" { sku = "2022-datacenter-azure-edition-core" } + lifecycle { + ignore_changes = [storage_path_id] + } + depends_on = [azurerm_role_assignment.test] } `, template, data.RandomString, os.Getenv(customLocationIdEnv), r.imageVersion) @@ -189,6 +193,10 @@ resource "azurerm_stack_hci_marketplace_gallery_image" "import" { offer = azurerm_stack_hci_marketplace_gallery_image.test.identifier.0.offer sku = azurerm_stack_hci_marketplace_gallery_image.test.identifier.0.sku } + + lifecycle { + ignore_changes = [storage_path_id] + } } `, config) } diff --git a/internal/services/azurestackhci/stack_hci_network_interface_resource.go b/internal/services/azurestackhci/stack_hci_network_interface_resource.go index 5b8ff469e4c0..170b406e6586 100644 --- a/internal/services/azurestackhci/stack_hci_network_interface_resource.go +++ b/internal/services/azurestackhci/stack_hci_network_interface_resource.go @@ -366,7 +366,7 @@ func resourceNetworkInterfaceWaitForCreated(ctx context.Context, client networki state := &pluginsdk.StateChangeConf{ MinTimeout: 10 * time.Second, - ContinuousTargetOccurence: 3, + ContinuousTargetOccurence: 4, Pending: []string{"NotFound"}, Target: []string{"Found"}, Refresh: resourceNetworkInterfaceRefreshFunc(ctx, client, id), diff --git a/internal/services/azurestackhci/stack_hci_network_interface_resource_test.go b/internal/services/azurestackhci/stack_hci_network_interface_resource_test.go index 15365695612e..c82ea6871df6 100644 --- a/internal/services/azurestackhci/stack_hci_network_interface_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_network_interface_resource_test.go @@ -163,7 +163,7 @@ resource "azurerm_stack_hci_network_interface" "test" { } lifecycle { - ignore_changes = [mac_address] + ignore_changes = [mac_address, ip_configuration.0.private_ip_address] } } `, template, data.RandomString, os.Getenv(customLocationIdEnv)) @@ -184,6 +184,10 @@ resource "azurerm_stack_hci_network_interface" "import" { ip_configuration { subnet_id = azurerm_stack_hci_network_interface.test.ip_configuration.0.subnet_id } + + lifecycle { + ignore_changes = [mac_address, ip_configuration.0.private_ip_address] + } } `, config) } diff --git a/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go b/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go index c2f5aa78a07c..15409627be5a 100644 --- a/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go +++ b/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go @@ -333,7 +333,7 @@ func resourceVirtualHardDiskWaitForCreated(ctx context.Context, client virtualha state := &pluginsdk.StateChangeConf{ MinTimeout: 10 * time.Second, - ContinuousTargetOccurence: 3, + ContinuousTargetOccurence: 4, Pending: []string{"NotFound"}, Target: []string{"Found"}, Refresh: resourceVirtualHardDiskRefreshFunc(ctx, client, id), diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go index dca75a507a73..90e51808d5e4 100644 --- a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go @@ -54,7 +54,7 @@ type StackHCIWindowsVirtualMachineResourceModel struct { OsProfile []StackHCIVirtualMachineOsProfileWindows `tfschema:"os_profile"` StorageProfile []StackHCIVirtualMachineStorageProfile `tfschema:"storage_profile"` - // securityProfile + // securityProfile is flattened, since it is always returned from API SecureBootEnabled bool `tfschema:"secure_boot_enabled"` SecurityType string `tfschema:"security_type"` TpmEnabled bool `tfschema:"tpm_enabled"` diff --git a/website/docs/r/stack_hci_marketplace_gallery_image.html.markdown b/website/docs/r/stack_hci_marketplace_gallery_image.html.markdown index f620edf9279c..568cc8aed870 100644 --- a/website/docs/r/stack_hci_marketplace_gallery_image.html.markdown +++ b/website/docs/r/stack_hci_marketplace_gallery_image.html.markdown @@ -48,6 +48,7 @@ resource "azurerm_stack_hci_marketplace_gallery_image" "example" { foo = "bar" env = "example" } + depends_on = [azurerm_role_assignment.example] } ``` @@ -75,6 +76,8 @@ The following arguments are supported: * `storage_path_id` - (Optional) The ID of the Azure Stack HCI Storage Path used for this Marketplace Gallery Image. Changing this forces a new Azure Stack HCI Virtual Hard Disk to be created. +-> **Note:** If `storage_path_id` is not specified, it will be assigned by the server. If you experience a diff you may need to add this to `ignore_changes`. + * `tags` - (Optional) A mapping of tags which should be assigned to the Azure Stack HCI Marketplace Gallery Image. --- diff --git a/website/docs/r/stack_hci_network_interface.html.markdown b/website/docs/r/stack_hci_network_interface.html.markdown index 01709b058055..264a4abd60bc 100644 --- a/website/docs/r/stack_hci_network_interface.html.markdown +++ b/website/docs/r/stack_hci_network_interface.html.markdown @@ -94,6 +94,8 @@ An `ip_configuration` block supports the following: * `private_ip_address` - (Optional) The IPv4 address of the IP configuration. Changing this forces a new resource to be created. +-> **Note:** If `private_ip_address` is not specified, it will be assigned by the server. If you experience a diff you may need to add this to `ignore_changes`. + ## Attributes Reference In addition to the Arguments listed above - the following Attributes are exported: diff --git a/website/docs/r/stack_hci_virtual_hard_disk.html.markdown b/website/docs/r/stack_hci_virtual_hard_disk.html.markdown index a25d4f037603..059d61d1d357 100644 --- a/website/docs/r/stack_hci_virtual_hard_disk.html.markdown +++ b/website/docs/r/stack_hci_virtual_hard_disk.html.markdown @@ -72,7 +72,7 @@ The following arguments are supported: * `storage_path_id` - (Optional) The ID of the Azure Stack HCI Storage Path used for this Virtual Hard Disk. Changing this forces a new Azure Stack HCI Virtual Hard Disk to be created. --> **Note:** If `storage_path_id` is not specified, the Virtual Hard Disk will be placed in a high availability Storage Path. If you experience a diff you may need to add this to `ignore_changes`. +-> **Note:** If `storage_path_id` is not specified, it will be assigned by the server. If you experience a diff you may need to add this to `ignore_changes`. * `tags` - (Optional) A mapping of tags which should be assigned to the Azure Stack HCI Virtual Hard Disk. diff --git a/website/docs/r/stack_hci_windows_virtual_machine.html.markdown b/website/docs/r/stack_hci_windows_virtual_machine.html.markdown index 70730a2e33d1..cfe90ed686d0 100644 --- a/website/docs/r/stack_hci_windows_virtual_machine.html.markdown +++ b/website/docs/r/stack_hci_windows_virtual_machine.html.markdown @@ -3,38 +3,139 @@ subcategory: "Azure Stack HCI" layout: "azurerm" page_title: "Azure Resource Manager: azurerm_stack_hci_windows_virtual_machine" description: |- - Manages a Stack HCI Windows Virtual Machine. + Manages an Azure Stack HCI Windows Virtual Machine. --- # azurerm_stack_hci_windows_virtual_machine -Manages a Stack HCI Windows Virtual Machine. +Manages an Azure Stack HCI Windows Virtual Machine. ## Example Usage ```hcl -resource "azurerm_stack_hci_windows_virtual_machine" "example" { +resource "azurerm_resource_group" "example" { + name = "example-rg" + location = "West Europe" +} - network_profile { - network_interface_ids = [ "example" ] +resource "azurerm_stack_hci_logical_network" "example" { + name = "example-ln" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + custom_location_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ExtendedLocation/customLocations/cl1" + virtual_switch_name = "ConvergedSwitch(managementcompute)" + dns_servers = ["192.168.1.254"] + + subnet { + ip_allocation_method = "Static" + address_prefix = "192.168.1.0/24" + ip_pool { + start = "192.168.1.0" + end = "192.168.1.255" + } + + route { + address_prefix = "0.0.0.0/0" + next_hop_ip_address = "192.168.1.1" + } + } +} + +resource "azurerm_stack_hci_network_interface" "example" { + name = "example-ni" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + custom_location_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ExtendedLocation/customLocations/cl1" + dns_servers = ["192.168.1.254"] + mac_address = "02:ec:01:0c:00:08" + + ip_configuration { + private_ip_address = "192.168.1.15" + subnet_id = azurerm_stack_hci_logical_network.example.id + } +} + +// service principal of 'Microsoft.AzureStackHCI Resource Provider' +data "azuread_service_principal" "hciRp" { + client_id = "1412d89f-b8a8-4111-b4fd-e82905cbd85d" +} + +resource "azurerm_role_assignment" "example" { + scope = azurerm_resource_group.example.id + role_definition_name = "Azure Connected Machine Resource Manager" + principal_id = data.azuread_service_principal.hciRp.object_id +} + +resource "azurerm_stack_hci_virtual_hard_disk" "example" { + name = "example-vhd" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + custom_location_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ExtendedLocation/customLocations/cl1" + disk_size_in_gb = 2 + + lifecycle { + ignore_changes = [storage_path_id] + } +} + +resource "azurerm_stack_hci_marketplace_gallery_image" "example" { + name = "example-mgi" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + custom_location_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ExtendedLocation/customLocations/cl1" + hyperv_generation = "V2" + os_type = "Windows" + version = "20348.2655.240905" + identifier { + publisher = "MicrosoftWindowsServer" + offer = "WindowsServer" + sku = "2022-datacenter-azure-edition-core" + } + tags = { + foo = "bar" + env = "example" + } + depends_on = [azurerm_role_assignment.example] +} + +resource "azurerm_arc_machine" "example" { + name = "example-hcivm" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + kind = "HCI" + + identity { + type = "SystemAssigned" } - custom_location_id = "TODO" - arc_machine_id = "TODO" +} + +resource "azurerm_stack_hci_windows_virtual_machine" "example" { + arc_machine_id = azurerm_stack_hci_windows_virtual_machine.example.arc_machine_id + custom_location_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ExtendedLocation/customLocations/cl1" hardware_profile { - vm_size = "TODO" - processor_number = 42 - memory_mb = 42 + vm_size = "Custom" + processor_number = 2 + memory_mb = 8192 + } + + network_profile { + network_interface_ids = [azurerm_stack_hci_network_interface.example.id] } os_profile { - admin_username = "TODO" - computer_name = "example" + admin_username = "adminuser" + admin_password = "!password!@#$" + computer_name = "examplevm" } storage_profile { - data_disk_ids = [ "example" ] - image_id = "TODO" + data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.example.id] + image_id = azurerm_stack_hci_marketplace_gallery_image.example.id + } + + lifecycle { + ignore_changes = [storage_profile.0.vm_config_storage_path_id] } } ``` @@ -43,127 +144,130 @@ resource "azurerm_stack_hci_windows_virtual_machine" "example" { The following arguments are supported: -* `arc_machine_id` - (Required) The ID of the TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `arc_machine_id` - (Required) The ID of the Arc Machine. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `custom_location_id` - (Required) The ID of the TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `custom_location_id` - (Required) The ID of the Custom Location. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `hardware_profile` - (Required) A `hardware_profile` block as defined below. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `hardware_profile` - (Required) A `hardware_profile` block as defined below. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. * `network_profile` - (Required) A `network_profile` block as defined below. -* `os_profile` - (Required) A `os_profile` block as defined below. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `os_profile` - (Required) An `os_profile` block as defined below. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. * `storage_profile` - (Required) A `storage_profile` block as defined below. --- -* `http_proxy_configuration` - (Optional) A `http_proxy_configuration` block as defined below. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `http_proxy_configuration` - (Optional) A `http_proxy_configuration` block as defined below. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `secure_boot_enabled` - (Optional) Should the TODO be enabled? Defaults to `true`. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `secure_boot_enabled` - (Optional) Whether to enable secure boot. Defaults to `true`. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `security_type` - (Optional) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `security_type` - (Optional) The security type of the virtual machine. Possible values are `TrustedLaunch` and `ConfidentialVM`. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `tpm_enabled` - (Optional) Should the TODO be enabled? Defaults to `false`. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `tpm_enabled` - (Optional) Whether to enable the TPM (Trusted Platform Module). Defaults to `false`. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. --- A `dynamic_memory` block supports the following: -* `maximum_memory_mb` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `maximum_memory_mb` - (Required) The maximum memory in Megabytes . Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `minimum_memory_mb` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `minimum_memory_mb` - (Required) The minimum memory in Megabytes. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `target_memory_buffer` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `target_memory_buffer` - (Required) The extra memory that should be reserved for a virtual machine instance at runtime. Possible value can be in the range of `5` to `2000`. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. --- A `hardware_profile` block supports the following: -* `memory_mb` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `memory_mb` - (Required) The memory in Megabytes. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `processor_number` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `processor_number` - (Required) The number of processors. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `vm_size` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `vm_size` - (Required) The size of virtual machine. Possible values are +`Custom`, `Default`, `Standard_A4_v2`, `Standard_A2_v2`, `Standard_D8s_v3`, `Standard_D4s_v3`, `Standard_D16s_v3`, `Standard_DS5_v2`, `Standard_DS4_v2`, `Standard_DS13_v2`, `Standard_DS3_v2`, `Standard_DS2_v2`, `Standard_D32s_v3`, `Standard_D2s_v3`, `Standard_K8S5_v1`, `Standard_K8S4_v1`, `Standard_K8S3_v1`, `Standard_K8S2_v1`, `Standard_K8S_v1`, `Standard_NK12`, `Standard_NK6`, `Standard_NV12` and `Standard_NV6`. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `dynamic_memory` - (Optional) A `dynamic_memory` block as defined above. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `dynamic_memory` - (Optional) A `dynamic_memory` block as defined above. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. --- A `http_proxy_configuration` block supports the following: -* `http_proxy` - (Optional) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `http_proxy` - (Optional) The HTTP proxy server endpoint. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `https_proxy` - (Optional) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `https_proxy` - (Optional) The HTTPS proxy server endpoint. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `no_proxy` - (Optional) Specifies a list of TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `no_proxy` - (Optional) Specifies a list of endpoints that should not go through proxy. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `trusted_ca` - (Optional) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `trusted_ca` - (Optional) Alternative CA cert to use for connecting to proxy servers. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. --- A `network_profile` block supports the following: -* `network_interface_ids` - (Required) Specifies a list of TODO. +* `network_interface_ids` - (Required) Specifies a list of Azure Stack HCI Network Interface IDs. --- -A `os_profile` block supports the following: +An `os_profile` block supports the following: -* `admin_username` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `admin_username` - (Required) The username of admin. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `computer_name` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `computer_name` - (Required) The computer name. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `admin_password` - (Optional) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `admin_password` - (Optional) The password of admin. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `automatic_update_enabled` - (Optional) Should the TODO be enabled? Defaults to `false`. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `automatic_update_enabled` - (Optional) Whether automatic update is enabled. Defaults to `false`. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `provision_vm_agent_enabled` - (Optional) Should the TODO be enabled? Defaults to `false`. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `provision_vm_agent_enabled` - (Optional) Whether the Arc VM Agent should be installed during the virtual machine creation process. Defaults to `false`. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `provision_vm_config_agent_enabled` - (Optional) Should the TODO be enabled? Defaults to `false`. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `provision_vm_config_agent_enabled` - (Optional) Whether the VM Config Agent should be installed during the virtual machine creation process. Defaults to `false`. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `ssh_public_key` - (Optional) One or more `ssh_public_key` blocks as defined below. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `ssh_public_key` - (Optional) One or more `ssh_public_key` blocks as defined below. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `time_zone` - (Optional) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `time_zone` - (Optional) The timezone for the virtual machine. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. --- A `ssh_public_key` block supports the following: -* `key_data` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `key_data` - (Required) SSH public key certificate used to authenticate with the VM through SSH. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `path` - (Required) TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `path` - (Required) The full path on the created VM where ssh public key is stored. If the file already exists, the specified key is appended to the file. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. --- A `storage_profile` block supports the following: -* `data_disk_ids` - (Required) Specifies a list of TODO. +* `data_disk_ids` - (Required) Specifies a list of Azure Stack HCI Virtual Hard Disk IDs. -* `image_id` - (Required) The ID of the TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `image_id` - (Required) The ID of the Stack HCI VM Image. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `os_disk_id` - (Optional) The ID of the TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `os_disk_id` - (Optional) The ID of the OS disk. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `vm_config_storage_path_id` - (Optional) The ID of the TODO. Changing this forces a new Stack HCI Windows Virtual Machine to be created. +* `vm_config_storage_path_id` - (Optional) The ID of the Azure Stack HCI Storage Path to host the VM configuration file. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. + +-> **Note:** If `vm_config_storage_path_id` is not specified, it will be assigned by the server. If you experience a diff you may need to add this to `ignore_changes`. ## Attributes Reference In addition to the Arguments listed above - the following Attributes are exported: -* `id` - The ID of the Stack HCI Windows Virtual Machine. +* `id` - The ID of the Azure Stack HCI Windows Virtual Machine. ## Timeouts The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: -* `create` - (Defaults to 1 hour and 30 minutes) Used when creating the Stack HCI Windows Virtual Machine. -* `read` - (Defaults to 5 minutes) Used when retrieving the Stack HCI Windows Virtual Machine. -* `update` - (Defaults to 1 hour and 30 minutes) Used when updating the Stack HCI Windows Virtual Machine. -* `delete` - (Defaults to 30 minutes) Used when deleting the Stack HCI Windows Virtual Machine. +* `create` - (Defaults to 90 minutes) Used when creating the Azure Stack HCI Windows Virtual Machine. +* `read` - (Defaults to 5 minutes) Used when retrieving the Azure Stack HCI Windows Virtual Machine. +* `update` - (Defaults to 1 hour and 30 minutes) Used when updating the Azure Stack HCI Windows Virtual Machine. +* `delete` - (Defaults to 30 minutes) Used when deleting the Azure Stack HCI Windows Virtual Machine. ## Import -Stack HCI Windows Virtual Machines can be imported using the `resource id`, e.g. +Azure Stack HCI Windows Virtual Machines can be imported using the `resource id`, e.g. ```shell terraform import azurerm_stack_hci_windows_virtual_machine.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.AzureStackHCI/virtualMachineInstances/default -``` \ No newline at end of file +``` From 2c35a67e73498b4e1429a37198243e92d0adea86 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Wed, 18 Dec 2024 08:28:12 +0000 Subject: [PATCH 11/16] go generate --- .../azurestackhci/parse/stack_hci_virtual_machine.go | 11 ++++++----- .../parse/stack_hci_virtual_machine_test.go | 2 -- .../validate/stack_hci_virtual_machine_id_test.go | 1 - 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/internal/services/azurestackhci/parse/stack_hci_virtual_machine.go b/internal/services/azurestackhci/parse/stack_hci_virtual_machine.go index a7b9bf0851b6..65b000531504 100644 --- a/internal/services/azurestackhci/parse/stack_hci_virtual_machine.go +++ b/internal/services/azurestackhci/parse/stack_hci_virtual_machine.go @@ -6,6 +6,7 @@ package parse // NOTE: this file is generated via 'go:generate' - manual changes will be overwritten import ( + "errors" "fmt" "strings" @@ -35,7 +36,7 @@ func (id StackHCIVirtualMachineId) String() string { fmt.Sprintf("Resource Group %q", id.ResourceGroup), } segmentsStr := strings.Join(segments, " / ") - return fmt.Sprintf("%s: (%s)", "Stack H C I Virtual Machine", segmentsStr) + return fmt.Sprintf("%s: (%s)", "StackHCI Virtual Machine", segmentsStr) } func (id StackHCIVirtualMachineId) ID() string { @@ -56,11 +57,11 @@ func StackHCIVirtualMachineID(input string) (*StackHCIVirtualMachineId, error) { } if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + return nil, errors.New("ID was missing the 'subscriptions' element") } if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + return nil, errors.New("ID was missing the 'resourceGroups' element") } if resourceId.MachineName, err = id.PopSegment("machines"); err != nil { @@ -95,11 +96,11 @@ func StackHCIVirtualMachineIDInsensitively(input string) (*StackHCIVirtualMachin } if resourceId.SubscriptionId == "" { - return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + return nil, errors.New("ID was missing the 'subscriptions' element") } if resourceId.ResourceGroup == "" { - return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + return nil, errors.New("ID was missing the 'resourceGroups' element") } // find the correct casing for the 'machines' segment diff --git a/internal/services/azurestackhci/parse/stack_hci_virtual_machine_test.go b/internal/services/azurestackhci/parse/stack_hci_virtual_machine_test.go index 16befe469efa..060666b6026c 100644 --- a/internal/services/azurestackhci/parse/stack_hci_virtual_machine_test.go +++ b/internal/services/azurestackhci/parse/stack_hci_virtual_machine_test.go @@ -27,7 +27,6 @@ func TestStackHCIVirtualMachineID(t *testing.T) { Error bool Expected *StackHCIVirtualMachineId }{ - { // empty Input: "", @@ -136,7 +135,6 @@ func TestStackHCIVirtualMachineIDInsensitively(t *testing.T) { Error bool Expected *StackHCIVirtualMachineId }{ - { // empty Input: "", diff --git a/internal/services/azurestackhci/validate/stack_hci_virtual_machine_id_test.go b/internal/services/azurestackhci/validate/stack_hci_virtual_machine_id_test.go index b05f62f59b68..1508294bdbb4 100644 --- a/internal/services/azurestackhci/validate/stack_hci_virtual_machine_id_test.go +++ b/internal/services/azurestackhci/validate/stack_hci_virtual_machine_id_test.go @@ -12,7 +12,6 @@ func TestStackHCIVirtualMachineID(t *testing.T) { Input string Valid bool }{ - { // empty Input: "", From 37e433d8aebe8d75f865b50691924d5284f8ed4c Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Wed, 18 Dec 2024 08:33:18 +0000 Subject: [PATCH 12/16] link API issue --- .../stack_hci_marketplace_gallery_image_resource.go | 1 + .../azurestackhci/stack_hci_network_interface_resource.go | 3 ++- .../azurestackhci/stack_hci_virtual_hard_disk_resource.go | 3 ++- .../stack_hci_windows_virtual_machine_resource.go | 3 ++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource.go b/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource.go index a927b8ac4c13..062386f83c86 100644 --- a/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource.go +++ b/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource.go @@ -194,6 +194,7 @@ func (r StackHCIMarketplaceGalleryImageResource) Create() sdk.ResourceFunc { return fmt.Errorf("performing create %s: %+v", id, err) } + // https://github.com/Azure/azure-rest-api-specs/issues/31876 if err := resourceMarketplaceGalleryImageWaitForCreated(ctx, *client, id); err != nil { return fmt.Errorf("waiting for %s to be created: %+v", id, err) } diff --git a/internal/services/azurestackhci/stack_hci_network_interface_resource.go b/internal/services/azurestackhci/stack_hci_network_interface_resource.go index 170b406e6586..49c6d3d580f8 100644 --- a/internal/services/azurestackhci/stack_hci_network_interface_resource.go +++ b/internal/services/azurestackhci/stack_hci_network_interface_resource.go @@ -182,8 +182,9 @@ func (r StackHCINetworkInterfaceResource) Create() sdk.ResourceFunc { return fmt.Errorf("performing create %s: %+v", id, err) } + // https://github.com/Azure/azure-rest-api-specs/issues/31876 if err := resourceNetworkInterfaceWaitForCreated(ctx, *client, id); err != nil { - return err + return fmt.Errorf("waiting for %s to be created: %+v", id, err) } metadata.SetID(id) diff --git a/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go b/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go index 15409627be5a..34af6f6219e7 100644 --- a/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go +++ b/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go @@ -210,8 +210,9 @@ func (r StackHCIVirtualHardDiskResource) Create() sdk.ResourceFunc { return fmt.Errorf("performing create %s: %+v", id, err) } + // https://github.com/Azure/azure-rest-api-specs/issues/31876 if err := resourceVirtualHardDiskWaitForCreated(ctx, *client, id); err != nil { - return err + return fmt.Errorf("waiting for %s to be created: %+v", id, err) } metadata.SetID(id) diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go index 90e51808d5e4..ad8008e5c35c 100644 --- a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go @@ -443,8 +443,9 @@ func (r StackHCIWindowsVirtualMachineResource) Create() sdk.ResourceFunc { return fmt.Errorf("creating %s: %+v", id, err) } + // https://github.com/Azure/azure-rest-api-specs/issues/31876 if err := resourceVirtualMachineWaitForCreated(ctx, *client, id); err != nil { - return err + return fmt.Errorf("waiting for %s to be created: %+v", id, err) } metadata.SetID(id) From fa76c824bc4213b4ac9906deb7d7d55151198b91 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Wed, 18 Dec 2024 08:45:03 +0000 Subject: [PATCH 13/16] fix lint --- ...i_windows_virtual_machine_resource_test.go | 40 +++++++++---------- ..._hci_windows_virtual_machine.html.markdown | 4 +- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go index 027a49fb7f41..b709fdaaef3b 100644 --- a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go @@ -198,7 +198,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { storage_profile { data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id] - image_id = %[4]q + image_id = %[4]q } depends_on = [azurerm_role_assignment.test] @@ -237,7 +237,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "import" { storage_profile { data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id] - image_id = %[4]q + image_id = %[4]q } depends_on = [azurerm_role_assignment.test] @@ -271,7 +271,7 @@ resource "azurerm_stack_hci_virtual_hard_disk" "test2" { disk_size_in_gb = 2 lifecycle { - ignore_changes = [storage_path_id] + ignore_changes = [storage_path_id] create_before_destroy = true } } @@ -287,14 +287,14 @@ resource "azurerm_stack_hci_network_interface" "test2" { } lifecycle { - ignore_changes = [mac_address, ip_configuration.0.private_ip_address] + ignore_changes = [mac_address, ip_configuration.0.private_ip_address] create_before_destroy = true } } resource "azurerm_stack_hci_windows_virtual_machine" "test" { - arc_machine_id = azurerm_arc_machine.test.id - custom_location_id = %[3]q + arc_machine_id = azurerm_arc_machine.test.id + custom_location_id = %[3]q hardware_profile { vm_size = "Custom" @@ -307,14 +307,14 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { } os_profile { - admin_username = "adminuser" - admin_password = "!password!@#$" - computer_name = "testvm" + admin_username = "adminuser" + admin_password = "!password!@#$" + computer_name = "testvm" } storage_profile { data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id, azurerm_stack_hci_virtual_hard_disk.test2.id] - image_id = %[4]q + image_id = %[4]q } depends_on = [azurerm_role_assignment.test] @@ -402,19 +402,19 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { admin_username = "adminuser" admin_password = "!password!@#$" computer_name = "testvm2" - automatic_update_enabled = true - time_zone = "UTC" - provision_vm_agent_enabled = true - provision_vm_config_agent_enabled = true - ssh_public_key { - path = "C:\\Users\\adminuser\\.ssh\\rsa.pub" - key_data = tls_private_key.rsa-4096-example.public_key_openssh - } + automatic_update_enabled = true + time_zone = "UTC" + provision_vm_agent_enabled = true + provision_vm_config_agent_enabled = true + ssh_public_key { + path = "C:\\Users\\adminuser\\.ssh\\rsa.pub" + key_data = tls_private_key.rsa-4096-example.public_key_openssh + } } storage_profile { - data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id] - image_id = %[5]q + data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.test.id] + image_id = %[5]q vm_config_storage_path_id = azurerm_stack_hci_storage_path.test.id } diff --git a/website/docs/r/stack_hci_windows_virtual_machine.html.markdown b/website/docs/r/stack_hci_windows_virtual_machine.html.markdown index cfe90ed686d0..49e28b9c979f 100644 --- a/website/docs/r/stack_hci_windows_virtual_machine.html.markdown +++ b/website/docs/r/stack_hci_windows_virtual_machine.html.markdown @@ -111,7 +111,7 @@ resource "azurerm_arc_machine" "example" { resource "azurerm_stack_hci_windows_virtual_machine" "example" { arc_machine_id = azurerm_stack_hci_windows_virtual_machine.example.arc_machine_id - custom_location_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ExtendedLocation/customLocations/cl1" + custom_location_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ExtendedLocation/customLocations/cl1" hardware_profile { vm_size = "Custom" @@ -131,7 +131,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "example" { storage_profile { data_disk_ids = [azurerm_stack_hci_virtual_hard_disk.example.id] - image_id = azurerm_stack_hci_marketplace_gallery_image.example.id + image_id = azurerm_stack_hci_marketplace_gallery_image.example.id } lifecycle { From cab84b5d6b63f8cbe2b558d2c03ea330912645ba Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Mon, 30 Dec 2024 06:39:03 +0000 Subject: [PATCH 14/16] fix --- ...ck_hci_windows_virtual_machine_resource.go | 97 ++++++------------- ...i_windows_virtual_machine_resource_test.go | 17 ++-- ..._hci_windows_virtual_machine.html.markdown | 20 ++-- 3 files changed, 49 insertions(+), 85 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go index ad8008e5c35c..648639283bf9 100644 --- a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "log" - "regexp" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" @@ -22,6 +21,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/services/azurestackhci/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/azurestackhci/validate" + computeValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/compute/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" ) @@ -62,15 +62,15 @@ type StackHCIWindowsVirtualMachineResourceModel struct { type StackHCIVirtualMachineHardwareProfile struct { DynamicMemory []StackHCIVirtualMachineDynamicMemory `tfschema:"dynamic_memory"` - MemoryMb int64 `tfschema:"memory_mb"` + MemoryInMb int64 `tfschema:"memory_in_mb"` ProcessorNumber int64 `tfschema:"processor_number"` VmSize string `tfschema:"vm_size"` } type StackHCIVirtualMachineDynamicMemory struct { - MaximumMemoryMb int64 `tfschema:"maximum_memory_mb"` - MinimumMemoryMb int64 `tfschema:"minimum_memory_mb"` - TargetMemoryBuffer int64 `tfschema:"target_memory_buffer"` + MaximumMemoryInMb int64 `tfschema:"maximum_memory_in_mb"` + MinimumMemoryInMb int64 `tfschema:"minimum_memory_in_mb"` + TargetMemoryBufferPercentage int64 `tfschema:"target_memory_buffer_percentage"` } type StackHCIVirtualMachineHttpProxyConfiguration struct { @@ -105,12 +105,9 @@ type StackHCIVirtualMachineSshPublicKey struct { type StackHCIVirtualMachineStorageProfile struct { DataDiskIds []string `tfschema:"data_disk_ids"` ImageId string `tfschema:"image_id"` - OsDiskId string `tfschema:"os_disk_id"` VmConfigStoragePathId string `tfschema:"vm_config_storage_path_id"` } -type StackHCIVirtualMachineOsDisk struct{} - func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.Schema { return map[string]*pluginsdk.Schema{ "arc_machine_id": commonschema.ResourceIDReferenceRequiredForceNew(&machines.MachineId{}), @@ -138,7 +135,7 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S ValidateFunc: validation.IntAtLeast(1), }, - "memory_mb": { + "memory_in_mb": { Type: pluginsdk.TypeInt, Required: true, ForceNew: true, @@ -152,21 +149,21 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S MaxItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ - "maximum_memory_mb": { + "maximum_memory_in_mb": { Type: pluginsdk.TypeInt, Required: true, ForceNew: true, ValidateFunc: validation.IntAtLeast(1), }, - "minimum_memory_mb": { + "minimum_memory_in_mb": { Type: pluginsdk.TypeInt, Required: true, ForceNew: true, ValidateFunc: validation.IntAtLeast(1), }, - "target_memory_buffer": { + "target_memory_buffer_percentage": { Type: pluginsdk.TypeInt, Required: true, ForceNew: true, @@ -189,10 +186,7 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S Type: pluginsdk.TypeList, Required: true, MinItems: 1, - Elem: &pluginsdk.Schema{ - Type: pluginsdk.TypeString, - ValidateFunc: networkinterfaces.ValidateNetworkInterfaceID, - }, + Elem: commonschema.ResourceIDReferenceElem(&networkinterfaces.NetworkInterfaceId{}), }, }, }, @@ -209,7 +203,7 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S Type: pluginsdk.TypeString, Required: true, ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, + ValidateFunc: computeValidate.WindowsAdminUsername, }, "admin_password": { @@ -217,17 +211,14 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S Optional: true, ForceNew: true, Sensitive: true, - ValidateFunc: validation.StringIsNotEmpty, + ValidateFunc: computeValidate.WindowsAdminPassword, }, "computer_name": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringMatch( - regexp.MustCompile(`^[\-a-zA-Z0-9]{0,15}$`), - "computer_name must begin and end with an alphanumeric character, be between 2 and 15 characters in length and can only contain alphanumeric characters and hyphens.", - ), + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: computeValidate.WindowsComputerNameFull, }, "automatic_update_enabled": { @@ -315,10 +306,7 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S Type: pluginsdk.TypeList, Required: true, MinItems: 1, - Elem: &pluginsdk.Schema{ - Type: pluginsdk.TypeString, - ValidateFunc: virtualharddisks.ValidateVirtualHardDiskID, - }, + Elem: commonschema.ResourceIDReferenceElem(&virtualharddisks.VirtualHardDiskId{}), }, "image_id": { @@ -331,19 +319,9 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S ), }, - "os_disk_id": { - Type: pluginsdk.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: virtualharddisks.ValidateVirtualHardDiskID, - }, + "os_disk_id": commonschema.ResourceIDReferenceOptionalForceNew(&virtualharddisks.VirtualHardDiskId{}), - "vm_config_storage_path_id": { - Type: pluginsdk.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: storagecontainers.ValidateStorageContainerID, - }, + "vm_config_storage_path_id": commonschema.ResourceIDReferenceOptionalForceNew(&storagecontainers.StorageContainerId{}), }, }, }, @@ -357,17 +335,15 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S Schema: map[string]*pluginsdk.Schema{ "http_proxy": { Type: pluginsdk.TypeString, - Optional: true, + Required: true, ForceNew: true, - Sensitive: true, ValidateFunc: validation.StringIsNotEmpty, }, "https_proxy": { Type: pluginsdk.TypeString, - Optional: true, + Required: true, ForceNew: true, - Sensitive: true, ValidateFunc: validation.StringIsNotEmpty, }, @@ -385,6 +361,7 @@ func (StackHCIWindowsVirtualMachineResource) Arguments() map[string]*pluginsdk.S Type: pluginsdk.TypeString, Optional: true, ForceNew: true, + Sensitive: true, ValidateFunc: validation.StringIsNotEmpty, }, }, @@ -592,7 +569,7 @@ func expandVirtualMachineHardwareProfile(input []StackHCIVirtualMachineHardwareP v := input[0] output := &virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfile{ DynamicMemoryConfig: expandVirtualMachineDynamicMemory(v.DynamicMemory), - MemoryMB: pointer.To(v.MemoryMb), + MemoryMB: pointer.To(v.MemoryInMb), Processors: pointer.To(v.ProcessorNumber), VMSize: pointer.To(virtualmachineinstances.VMSizeEnum(v.VmSize)), } @@ -608,7 +585,7 @@ func flattenVirtualMachineHardwareProfile(input *virtualmachineinstances.Virtual return []StackHCIVirtualMachineHardwareProfile{ { DynamicMemory: flattenVirtualMachineDynamicMemory(input.DynamicMemoryConfig), - MemoryMb: pointer.From(input.MemoryMB), + MemoryInMb: pointer.From(input.MemoryMB), ProcessorNumber: pointer.From(input.Processors), VmSize: string(pointer.From(input.VMSize)), }, @@ -622,9 +599,9 @@ func expandVirtualMachineDynamicMemory(input []StackHCIVirtualMachineDynamicMemo v := input[0] output := &virtualmachineinstances.VirtualMachineInstancePropertiesHardwareProfileDynamicMemoryConfig{ - MaximumMemoryMB: pointer.To(v.MaximumMemoryMb), - MinimumMemoryMB: pointer.To(v.MinimumMemoryMb), - TargetMemoryBuffer: pointer.To(v.TargetMemoryBuffer), + MaximumMemoryMB: pointer.To(v.MaximumMemoryInMb), + MinimumMemoryMB: pointer.To(v.MinimumMemoryInMb), + TargetMemoryBuffer: pointer.To(v.TargetMemoryBufferPercentage), } return output @@ -642,9 +619,9 @@ func flattenVirtualMachineDynamicMemory(input *virtualmachineinstances.VirtualMa return []StackHCIVirtualMachineDynamicMemory{ { - MaximumMemoryMb: pointer.From(input.MaximumMemoryMB), - MinimumMemoryMb: pointer.From(input.MinimumMemoryMB), - TargetMemoryBuffer: pointer.From(input.TargetMemoryBuffer), + MaximumMemoryInMb: pointer.From(input.MaximumMemoryMB), + MinimumMemoryInMb: pointer.From(input.MinimumMemoryMB), + TargetMemoryBufferPercentage: pointer.From(input.TargetMemoryBuffer), }, } } @@ -688,7 +665,7 @@ func flattenVirtualMachineHttpProxyConfig(input *virtualmachineinstances.HTTPPro func expandVirtualMachineNetworkProfileForUpdate(input []StackHCIVirtualMachineNetworkProfile) *virtualmachineinstances.NetworkProfileUpdate { if len(input) == 0 { - return nil + return &virtualmachineinstances.NetworkProfileUpdate{} } networkInterfaces := make([]virtualmachineinstances.NetworkProfileUpdateNetworkInterfacesInlined, 0) @@ -841,7 +818,7 @@ func expandVirtualMachineSecurityProfile(input StackHCIWindowsVirtualMachineReso func expandVirtualMachineStorageProfileWindowsForUpdate(input []StackHCIVirtualMachineStorageProfile) *virtualmachineinstances.StorageProfileUpdate { if len(input) == 0 { - return nil + return &virtualmachineinstances.StorageProfileUpdate{} } v := input[0] @@ -884,10 +861,6 @@ func expandVirtualMachineStorageProfileWindows(input []StackHCIVirtualMachineSto }, } - if v.OsDiskId != "" { - output.OsDisk.Id = pointer.To(v.OsDiskId) - } - if v.VmConfigStoragePathId != "" { output.VMConfigStoragePathId = pointer.To(v.VmConfigStoragePathId) } @@ -914,16 +887,10 @@ func flattenVirtualMachineStorageProfileWindows(input *virtualmachineinstances.V imageId = pointer.From(input.ImageReference.Id) } - var osDiskId string - if input.OsDisk != nil { - osDiskId = pointer.From(input.OsDisk.Id) - } - return []StackHCIVirtualMachineStorageProfile{ { DataDiskIds: dataDiskIds, ImageId: imageId, - OsDiskId: osDiskId, VmConfigStoragePathId: pointer.From(input.VMConfigStoragePathId), }, } diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go index b709fdaaef3b..0830c7c634b4 100644 --- a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go @@ -151,7 +151,6 @@ func testAccStackHCIWindowsVirtualMachine_requiresImport(t *testing.T) { } func (r StackHCIWindowsVirtualMachineResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { - clusterClient := client.AzureStackHCI.VirtualMachineInstances id, err := parse.StackHCIVirtualMachineID(state.ID) if err != nil { return nil, err @@ -159,7 +158,7 @@ func (r StackHCIWindowsVirtualMachineResource) Exists(ctx context.Context, clien arcMachineId := machines.NewMachineID(id.SubscriptionId, id.ResourceGroup, id.MachineName) scopeId := commonids.NewScopeID(arcMachineId.ID()) - resp, err := clusterClient.Get(ctx, scopeId) + resp, err := client.AzureStackHCI.VirtualMachineInstances.Get(ctx, scopeId) if err != nil { return nil, fmt.Errorf("retrieving %s: %+v", id, err) } @@ -183,7 +182,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { hardware_profile { vm_size = "Custom" processor_number = 2 - memory_mb = 8192 + memory_in_mb = 8192 } network_profile { @@ -223,7 +222,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "import" { hardware_profile { vm_size = "Custom" processor_number = 2 - memory_mb = 8192 + memory_in_mb = 8192 } network_profile { @@ -299,7 +298,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { hardware_profile { vm_size = "Custom" processor_number = 2 - memory_mb = 8192 + memory_in_mb = 8192 } network_profile { @@ -386,11 +385,11 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { hardware_profile { vm_size = "Custom" processor_number = 2 - memory_mb = 8192 + memory_in_mb = 8192 dynamic_memory { - maximum_memory_mb = 8192 - minimum_memory_mb = 512 - target_memory_buffer = 20 + maximum_memory_in_mb = 8192 + minimum_memory_in_mb = 512 + target_memory_buffer_percentage = 20 } } diff --git a/website/docs/r/stack_hci_windows_virtual_machine.html.markdown b/website/docs/r/stack_hci_windows_virtual_machine.html.markdown index 49e28b9c979f..bfb0435da526 100644 --- a/website/docs/r/stack_hci_windows_virtual_machine.html.markdown +++ b/website/docs/r/stack_hci_windows_virtual_machine.html.markdown @@ -116,7 +116,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "example" { hardware_profile { vm_size = "Custom" processor_number = 2 - memory_mb = 8192 + memory_in_mb = 8192 } network_profile { @@ -170,21 +170,21 @@ The following arguments are supported: A `dynamic_memory` block supports the following: -* `maximum_memory_mb` - (Required) The maximum memory in Megabytes . Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. +* `maximum_memory_in_mb` - (Required) The maximum memory in Megabytes . Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `minimum_memory_mb` - (Required) The minimum memory in Megabytes. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. +* `minimum_memory_in_mb` - (Required) The minimum memory in Megabytes. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `target_memory_buffer` - (Required) The extra memory that should be reserved for a virtual machine instance at runtime. Possible value can be in the range of `5` to `2000`. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. +* `target_memory_buffer_percentage` - (Required) The percentage of total memory to reserve as extra memory for a virtual machine instance during runtime. Possible value can be in the range of `5` to `2000`. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. --- A `hardware_profile` block supports the following: -* `memory_mb` - (Required) The memory in Megabytes. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. +* `memory_in_mb` - (Required) The memory in Megabytes. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. * `processor_number` - (Required) The number of processors. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `vm_size` - (Required) The size of virtual machine. Possible values are +* `vm_size` - (Required) The size of virtual machine. Possible values are `Custom`, `Default`, `Standard_A4_v2`, `Standard_A2_v2`, `Standard_D8s_v3`, `Standard_D4s_v3`, `Standard_D16s_v3`, `Standard_DS5_v2`, `Standard_DS4_v2`, `Standard_DS13_v2`, `Standard_DS3_v2`, `Standard_DS2_v2`, `Standard_D32s_v3`, `Standard_D2s_v3`, `Standard_K8S5_v1`, `Standard_K8S4_v1`, `Standard_K8S3_v1`, `Standard_K8S2_v1`, `Standard_K8S_v1`, `Standard_NK12`, `Standard_NK6`, `Standard_NV12` and `Standard_NV6`. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. * `dynamic_memory` - (Optional) A `dynamic_memory` block as defined above. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. @@ -193,9 +193,9 @@ A `hardware_profile` block supports the following: A `http_proxy_configuration` block supports the following: -* `http_proxy` - (Optional) The HTTP proxy server endpoint. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. +* `http_proxy` - (Required) The HTTP proxy server endpoint. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `https_proxy` - (Optional) The HTTPS proxy server endpoint. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. +* `https_proxy` - (Required) The HTTPS proxy server endpoint. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. * `no_proxy` - (Optional) Specifies a list of endpoints that should not go through proxy. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. @@ -243,15 +243,13 @@ A `storage_profile` block supports the following: * `image_id` - (Required) The ID of the Stack HCI VM Image. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -* `os_disk_id` - (Optional) The ID of the OS disk. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. - * `vm_config_storage_path_id` - (Optional) The ID of the Azure Stack HCI Storage Path to host the VM configuration file. Changing this forces a new Azure Stack HCI Windows Virtual Machine to be created. -> **Note:** If `vm_config_storage_path_id` is not specified, it will be assigned by the server. If you experience a diff you may need to add this to `ignore_changes`. ## Attributes Reference -In addition to the Arguments listed above - the following Attributes are exported: +In addition to the Arguments listed above - the following Attributes are exported: * `id` - The ID of the Azure Stack HCI Windows Virtual Machine. From 15f5b43b30861c23c44f44c89b6e45437d88a7d2 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Mon, 30 Dec 2024 07:44:55 +0000 Subject: [PATCH 15/16] revert changes to other resources --- ..._hci_marketplace_gallery_image_resource.go | 45 ------------------- ...marketplace_gallery_image_resource_test.go | 16 +------ .../stack_hci_network_interface_resource.go | 45 ------------------- ...ack_hci_network_interface_resource_test.go | 6 +-- .../stack_hci_virtual_hard_disk_resource.go | 45 ------------------- ...ci_marketplace_gallery_image.html.markdown | 5 +-- .../stack_hci_network_interface.html.markdown | 2 - .../stack_hci_virtual_hard_disk.html.markdown | 4 +- 8 files changed, 6 insertions(+), 162 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource.go b/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource.go index 062386f83c86..517cc7ceb217 100644 --- a/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource.go +++ b/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource.go @@ -6,7 +6,6 @@ package azurestackhci import ( "context" "fmt" - "log" "regexp" "time" @@ -194,11 +193,6 @@ func (r StackHCIMarketplaceGalleryImageResource) Create() sdk.ResourceFunc { return fmt.Errorf("performing create %s: %+v", id, err) } - // https://github.com/Azure/azure-rest-api-specs/issues/31876 - if err := resourceMarketplaceGalleryImageWaitForCreated(ctx, *client, id); err != nil { - return fmt.Errorf("waiting for %s to be created: %+v", id, err) - } - metadata.SetID(id) return nil @@ -336,42 +330,3 @@ func flattenStackHCIMarketplaceGalleryImageIdentifier(input *marketplacegalleryi }, } } - -func resourceMarketplaceGalleryImageWaitForCreated(ctx context.Context, client marketplacegalleryimages.MarketplaceGalleryImagesClient, id marketplacegalleryimages.MarketplaceGalleryImageId) error { - deadline, ok := ctx.Deadline() - if !ok { - return fmt.Errorf("internal error: context had no deadline") - } - - state := &pluginsdk.StateChangeConf{ - MinTimeout: 10 * time.Second, - ContinuousTargetOccurence: 3, - Pending: []string{"NotFound"}, - Target: []string{"Found"}, - Refresh: resourceMarketplaceGalleryImageRefreshFunc(ctx, client, id), - Timeout: time.Until(deadline), - } - - if _, err := state.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting for %s to be created: %+v", id, err) - } - - return nil -} - -func resourceMarketplaceGalleryImageRefreshFunc(ctx context.Context, client marketplacegalleryimages.MarketplaceGalleryImagesClient, id marketplacegalleryimages.MarketplaceGalleryImageId) pluginsdk.StateRefreshFunc { - return func() (interface{}, string, error) { - log.Printf("[DEBUG] Checking status for %s ..", id) - - resp, err := client.Get(ctx, id) - if err != nil { - if response.WasNotFound(resp.HttpResponse) { - return resp, "NotFound", nil - } - - return resp, "Error", fmt.Errorf("retrieving %s: %+v", id, err) - } - - return resp, "Found", nil - } -} diff --git a/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource_test.go b/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource_test.go index 00aed136f837..4776b7ee70b3 100644 --- a/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_marketplace_gallery_image_resource_test.go @@ -165,10 +165,6 @@ resource "azurerm_stack_hci_marketplace_gallery_image" "test" { sku = "2022-datacenter-azure-edition-core" } - lifecycle { - ignore_changes = [storage_path_id] - } - depends_on = [azurerm_role_assignment.test] } `, template, data.RandomString, os.Getenv(customLocationIdEnv), r.imageVersion) @@ -193,10 +189,6 @@ resource "azurerm_stack_hci_marketplace_gallery_image" "import" { offer = azurerm_stack_hci_marketplace_gallery_image.test.identifier.0.offer sku = azurerm_stack_hci_marketplace_gallery_image.test.identifier.0.sku } - - lifecycle { - ignore_changes = [storage_path_id] - } } `, config) } @@ -232,8 +224,6 @@ resource "azurerm_stack_hci_marketplace_gallery_image" "test" { offer = "WindowsServer" sku = "2022-datacenter-azure-edition-core" } - - depends_on = [azurerm_role_assignment.test] } `, template, data.RandomString, os.Getenv(customLocationIdEnv), r.imageVersion) } @@ -272,8 +262,6 @@ resource "azurerm_stack_hci_marketplace_gallery_image" "test" { tags = { foo = "bar" } - - depends_on = [azurerm_role_assignment.test] } `, template, data.RandomString, os.Getenv(customLocationIdEnv), r.imageVersion) } @@ -313,8 +301,6 @@ resource "azurerm_stack_hci_marketplace_gallery_image" "test" { foo = "bar" env = "test" } - - depends_on = [azurerm_role_assignment.test] } `, template, data.RandomString, os.Getenv(customLocationIdEnv), r.imageVersion) } @@ -326,6 +312,8 @@ resource "azurerm_resource_group" "test" { location = "%s" } +data "azurerm_client_config" "test" {} + // service principal of 'Microsoft.AzureStackHCI Resource Provider' data "azuread_service_principal" "hciRp" { client_id = "1412d89f-b8a8-4111-b4fd-e82905cbd85d" diff --git a/internal/services/azurestackhci/stack_hci_network_interface_resource.go b/internal/services/azurestackhci/stack_hci_network_interface_resource.go index 49c6d3d580f8..35ef99c81535 100644 --- a/internal/services/azurestackhci/stack_hci_network_interface_resource.go +++ b/internal/services/azurestackhci/stack_hci_network_interface_resource.go @@ -6,7 +6,6 @@ package azurestackhci import ( "context" "fmt" - "log" "regexp" "time" @@ -182,11 +181,6 @@ func (r StackHCINetworkInterfaceResource) Create() sdk.ResourceFunc { return fmt.Errorf("performing create %s: %+v", id, err) } - // https://github.com/Azure/azure-rest-api-specs/issues/31876 - if err := resourceNetworkInterfaceWaitForCreated(ctx, *client, id); err != nil { - return fmt.Errorf("waiting for %s to be created: %+v", id, err) - } - metadata.SetID(id) return nil @@ -358,42 +352,3 @@ func flattenStackHCINetworkInterfaceIPConfiguration(input *[]networkinterfaces.I return results, nil } - -func resourceNetworkInterfaceWaitForCreated(ctx context.Context, client networkinterfaces.NetworkInterfacesClient, id networkinterfaces.NetworkInterfaceId) error { - deadline, ok := ctx.Deadline() - if !ok { - return fmt.Errorf("internal error: context had no deadline") - } - - state := &pluginsdk.StateChangeConf{ - MinTimeout: 10 * time.Second, - ContinuousTargetOccurence: 4, - Pending: []string{"NotFound"}, - Target: []string{"Found"}, - Refresh: resourceNetworkInterfaceRefreshFunc(ctx, client, id), - Timeout: time.Until(deadline), - } - - if _, err := state.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting for %s to be created: %+v", id, err) - } - - return nil -} - -func resourceNetworkInterfaceRefreshFunc(ctx context.Context, client networkinterfaces.NetworkInterfacesClient, id networkinterfaces.NetworkInterfaceId) pluginsdk.StateRefreshFunc { - return func() (interface{}, string, error) { - log.Printf("[DEBUG] Checking status for %s ..", id) - - resp, err := client.Get(ctx, id) - if err != nil { - if response.WasNotFound(resp.HttpResponse) { - return resp, "NotFound", nil - } - - return resp, "Error", fmt.Errorf("retrieving %s: %+v", id, err) - } - - return resp, "Found", nil - } -} diff --git a/internal/services/azurestackhci/stack_hci_network_interface_resource_test.go b/internal/services/azurestackhci/stack_hci_network_interface_resource_test.go index c82ea6871df6..15365695612e 100644 --- a/internal/services/azurestackhci/stack_hci_network_interface_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_network_interface_resource_test.go @@ -163,7 +163,7 @@ resource "azurerm_stack_hci_network_interface" "test" { } lifecycle { - ignore_changes = [mac_address, ip_configuration.0.private_ip_address] + ignore_changes = [mac_address] } } `, template, data.RandomString, os.Getenv(customLocationIdEnv)) @@ -184,10 +184,6 @@ resource "azurerm_stack_hci_network_interface" "import" { ip_configuration { subnet_id = azurerm_stack_hci_network_interface.test.ip_configuration.0.subnet_id } - - lifecycle { - ignore_changes = [mac_address, ip_configuration.0.private_ip_address] - } } `, config) } diff --git a/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go b/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go index 34af6f6219e7..0c8f022d3050 100644 --- a/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go +++ b/internal/services/azurestackhci/stack_hci_virtual_hard_disk_resource.go @@ -6,7 +6,6 @@ package azurestackhci import ( "context" "fmt" - "log" "regexp" "time" @@ -210,11 +209,6 @@ func (r StackHCIVirtualHardDiskResource) Create() sdk.ResourceFunc { return fmt.Errorf("performing create %s: %+v", id, err) } - // https://github.com/Azure/azure-rest-api-specs/issues/31876 - if err := resourceVirtualHardDiskWaitForCreated(ctx, *client, id); err != nil { - return fmt.Errorf("waiting for %s to be created: %+v", id, err) - } - metadata.SetID(id) return nil @@ -325,42 +319,3 @@ func (r StackHCIVirtualHardDiskResource) Delete() sdk.ResourceFunc { }, } } - -func resourceVirtualHardDiskWaitForCreated(ctx context.Context, client virtualharddisks.VirtualHardDisksClient, id virtualharddisks.VirtualHardDiskId) error { - deadline, ok := ctx.Deadline() - if !ok { - return fmt.Errorf("internal error: context had no deadline") - } - - state := &pluginsdk.StateChangeConf{ - MinTimeout: 10 * time.Second, - ContinuousTargetOccurence: 4, - Pending: []string{"NotFound"}, - Target: []string{"Found"}, - Refresh: resourceVirtualHardDiskRefreshFunc(ctx, client, id), - Timeout: time.Until(deadline), - } - - if _, err := state.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting for %s to be created: %+v", id, err) - } - - return nil -} - -func resourceVirtualHardDiskRefreshFunc(ctx context.Context, client virtualharddisks.VirtualHardDisksClient, id virtualharddisks.VirtualHardDiskId) pluginsdk.StateRefreshFunc { - return func() (interface{}, string, error) { - log.Printf("[DEBUG] Checking status for %s ..", id) - - resp, err := client.Get(ctx, id) - if err != nil { - if response.WasNotFound(resp.HttpResponse) { - return resp, "NotFound", nil - } - - return resp, "Error", fmt.Errorf("retrieving %s: %+v", id, err) - } - - return resp, "Found", nil - } -} diff --git a/website/docs/r/stack_hci_marketplace_gallery_image.html.markdown b/website/docs/r/stack_hci_marketplace_gallery_image.html.markdown index 568cc8aed870..f3a681ebd109 100644 --- a/website/docs/r/stack_hci_marketplace_gallery_image.html.markdown +++ b/website/docs/r/stack_hci_marketplace_gallery_image.html.markdown @@ -48,7 +48,6 @@ resource "azurerm_stack_hci_marketplace_gallery_image" "example" { foo = "bar" env = "example" } - depends_on = [azurerm_role_assignment.example] } ``` @@ -76,8 +75,6 @@ The following arguments are supported: * `storage_path_id` - (Optional) The ID of the Azure Stack HCI Storage Path used for this Marketplace Gallery Image. Changing this forces a new Azure Stack HCI Virtual Hard Disk to be created. --> **Note:** If `storage_path_id` is not specified, it will be assigned by the server. If you experience a diff you may need to add this to `ignore_changes`. - * `tags` - (Optional) A mapping of tags which should be assigned to the Azure Stack HCI Marketplace Gallery Image. --- @@ -92,7 +89,7 @@ An `identifier` block supports the following: ## Attributes Reference -In addition to the Arguments listed above - the following Attributes are exported: +In addition to the Arguments listed above - the following Attributes are exported: * `id` - The ID of the Azure Stack HCI Marketplace Gallery Image. diff --git a/website/docs/r/stack_hci_network_interface.html.markdown b/website/docs/r/stack_hci_network_interface.html.markdown index 264a4abd60bc..01709b058055 100644 --- a/website/docs/r/stack_hci_network_interface.html.markdown +++ b/website/docs/r/stack_hci_network_interface.html.markdown @@ -94,8 +94,6 @@ An `ip_configuration` block supports the following: * `private_ip_address` - (Optional) The IPv4 address of the IP configuration. Changing this forces a new resource to be created. --> **Note:** If `private_ip_address` is not specified, it will be assigned by the server. If you experience a diff you may need to add this to `ignore_changes`. - ## Attributes Reference In addition to the Arguments listed above - the following Attributes are exported: diff --git a/website/docs/r/stack_hci_virtual_hard_disk.html.markdown b/website/docs/r/stack_hci_virtual_hard_disk.html.markdown index 059d61d1d357..1d156f3a6a2a 100644 --- a/website/docs/r/stack_hci_virtual_hard_disk.html.markdown +++ b/website/docs/r/stack_hci_virtual_hard_disk.html.markdown @@ -72,13 +72,13 @@ The following arguments are supported: * `storage_path_id` - (Optional) The ID of the Azure Stack HCI Storage Path used for this Virtual Hard Disk. Changing this forces a new Azure Stack HCI Virtual Hard Disk to be created. --> **Note:** If `storage_path_id` is not specified, it will be assigned by the server. If you experience a diff you may need to add this to `ignore_changes`. +-> **Note:** If `storage_path_id` is not specified, the Virtual Hard Disk will be placed in a high availability Storage Path. If you experience a diff you may need to add this to `ignore_changes`. * `tags` - (Optional) A mapping of tags which should be assigned to the Azure Stack HCI Virtual Hard Disk. ## Attributes Reference -In addition to the Arguments listed above - the following Attributes are exported: +In addition to the Arguments listed above - the following Attributes are exported: * `id` - The ID of the Azure Stack HCI Virtual Hard Disk. From 325f6ec9830c0c17a27e7be57a6f5653f78a518c Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Wed, 15 Jan 2025 09:16:24 +0000 Subject: [PATCH 16/16] fix test --- .../stack_hci_windows_virtual_machine_resource_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go index 0830c7c634b4..96ce710ae771 100644 --- a/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_windows_virtual_machine_resource_test.go @@ -191,7 +191,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { os_profile { admin_username = "adminuser" - admin_password = "!password!@#$" + admin_password = "!Password!@#$" computer_name = "testvm" } @@ -307,7 +307,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { os_profile { admin_username = "adminuser" - admin_password = "!password!@#$" + admin_password = "!Password!@#$" computer_name = "testvm" } @@ -399,7 +399,7 @@ resource "azurerm_stack_hci_windows_virtual_machine" "test" { os_profile { admin_username = "adminuser" - admin_password = "!password!@#$" + admin_password = "!Password!@#$" computer_name = "testvm2" automatic_update_enabled = true time_zone = "UTC"