diff --git a/internal/services/systemcentervirtualmachinemanager/parse/virtual_machine_instance.go b/internal/services/systemcentervirtualmachinemanager/parse/virtual_machine_instance.go new file mode 100644 index 000000000000..8d80107a0f95 --- /dev/null +++ b/internal/services/systemcentervirtualmachinemanager/parse/virtual_machine_instance.go @@ -0,0 +1,82 @@ +package parse + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.ResourceId = &SystemCenterVirtualMachineManagerVirtualMachineInstanceId{} + +type SystemCenterVirtualMachineManagerVirtualMachineInstanceId struct { + Scope string +} + +func NewSystemCenterVirtualMachineManagerVirtualMachineInstanceID(scope string) SystemCenterVirtualMachineManagerVirtualMachineInstanceId { + return SystemCenterVirtualMachineManagerVirtualMachineInstanceId{ + Scope: scope, + } +} + +func SystemCenterVirtualMachineManagerVirtualMachineInstanceID(input string) (*SystemCenterVirtualMachineManagerVirtualMachineInstanceId, error) { + parser := resourceids.NewParserFromResourceIdType(&SystemCenterVirtualMachineManagerVirtualMachineInstanceId{}) + parsed, err := parser.Parse(input, false) + if err != nil { + return nil, fmt.Errorf("parsing %q: %+v", input, err) + } + + id := SystemCenterVirtualMachineManagerVirtualMachineInstanceId{} + if err := id.FromParseResult(*parsed); err != nil { + return nil, err + } + + return &id, nil +} + +func SystemCenterVirtualMachineManagerVirtualMachineInstanceIDInsensitively(input string) (*SystemCenterVirtualMachineManagerVirtualMachineInstanceId, error) { + parser := resourceids.NewParserFromResourceIdType(&SystemCenterVirtualMachineManagerVirtualMachineInstanceId{}) + parsed, err := parser.Parse(input, true) + if err != nil { + return nil, fmt.Errorf("parsing %q: %+v", input, err) + } + + id := SystemCenterVirtualMachineManagerVirtualMachineInstanceId{} + if err := id.FromParseResult(*parsed); err != nil { + return nil, err + } + + return &id, nil +} + +func (id *SystemCenterVirtualMachineManagerVirtualMachineInstanceId) FromParseResult(input resourceids.ParseResult) error { + var ok bool + + if id.Scope, ok = input.Parsed["scope"]; !ok { + return resourceids.NewSegmentNotSpecifiedError(id, "scope", input) + } + + return nil +} + +func (id SystemCenterVirtualMachineManagerVirtualMachineInstanceId) ID() string { + fmtString := "/%s/providers/Microsoft.ScVmm/virtualMachineInstances/default" + return fmt.Sprintf(fmtString, strings.TrimPrefix(id.Scope, "/")) +} + +func (id SystemCenterVirtualMachineManagerVirtualMachineInstanceId) Segments() []resourceids.Segment { + return []resourceids.Segment{ + resourceids.ScopeSegment("scope", "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/some-resource-group/providers/Microsoft.HybridCompute/machines/some-machine"), + resourceids.StaticSegment("staticProviders", "providers", "providers"), + resourceids.ResourceProviderSegment("staticMicrosoftScVmm", "Microsoft.ScVmm", "Microsoft.ScVmm"), + resourceids.StaticSegment("staticVirtualMachineInstances", "virtualMachineInstances", "virtualMachineInstances"), + resourceids.StaticSegment("staticDefault", "default", "default"), + } +} + +func (id SystemCenterVirtualMachineManagerVirtualMachineInstanceId) String() string { + components := []string{ + fmt.Sprintf("Scope: %q", id.Scope), + } + return fmt.Sprintf("System Center Virtual Machine Manager Virtual Machine Instance (%s)", strings.Join(components, "\n")) +} diff --git a/internal/services/systemcentervirtualmachinemanager/parse/virtual_machine_instance_test.go b/internal/services/systemcentervirtualmachinemanager/parse/virtual_machine_instance_test.go new file mode 100644 index 000000000000..ac1eaf0f4004 --- /dev/null +++ b/internal/services/systemcentervirtualmachinemanager/parse/virtual_machine_instance_test.go @@ -0,0 +1,68 @@ +package parse + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = SystemCenterVirtualMachineManagerVirtualMachineInstanceId{} + +func TestSystemCenterVirtualMachineManagerVirtualMachineInstanceIDFormatter(t *testing.T) { + actual := NewSystemCenterVirtualMachineManagerVirtualMachineInstanceID("/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.HybridCompute/machines/machine1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.ScVmm/virtualMachineInstances/default" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestSystemCenterVirtualMachineManagerVirtualMachineInstanceID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *SystemCenterVirtualMachineManagerVirtualMachineInstanceId + }{ + { + Input: "", + Error: true, + }, + { + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.HybridCompute/machines/machine1", + Error: true, + }, + { + Input: "/providers/Microsoft.ScVmm/virtualMachineInstances/default", + Error: true, + }, + { + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.ScVmm/virtualMachineInstances/default", + Expected: &SystemCenterVirtualMachineManagerVirtualMachineInstanceId{ + Scope: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.HybridCompute/machines/machine1", + }, + }, + { + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.HYBRIDCOMPUTE/MACHINES/MACHINE1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := SystemCenterVirtualMachineManagerVirtualMachineInstanceID(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.Scope != v.Expected.Scope { + t.Fatalf("Expected %q but got %q for ScopeId", v.Expected.Scope, actual.Scope) + } + } +} diff --git a/internal/services/systemcentervirtualmachinemanager/registration.go b/internal/services/systemcentervirtualmachinemanager/registration.go index 55269f7265bf..f62e9b63d356 100644 --- a/internal/services/systemcentervirtualmachinemanager/registration.go +++ b/internal/services/systemcentervirtualmachinemanager/registration.go @@ -38,6 +38,7 @@ func (r Registration) Resources() []sdk.Resource { SystemCenterVirtualMachineManagerCloudResource{}, SystemCenterVirtualMachineManagerServerResource{}, SystemCenterVirtualMachineManagerAvailabilitySetResource{}, + SystemCenterVirtualMachineManagerVirtualMachineInstanceResource{}, SystemCenterVirtualMachineManagerVirtualNetworkResource{}, SystemCenterVirtualMachineManagerVirtualMachineTemplateResource{}, } diff --git a/internal/services/systemcentervirtualmachinemanager/system_center_virtual_machine_manager_virtual_machine_instance_resource.go b/internal/services/systemcentervirtualmachinemanager/system_center_virtual_machine_manager_virtual_machine_instance_resource.go new file mode 100644 index 000000000000..4a02712adf2a --- /dev/null +++ b/internal/services/systemcentervirtualmachinemanager/system_center_virtual_machine_manager_virtual_machine_instance_resource.go @@ -0,0 +1,986 @@ +package systemcentervirtualmachinemanager + +import ( + "context" + "fmt" + "strconv" + "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-sdk/resource-manager/extendedlocation/2021-08-15/customlocations" + "github.com/hashicorp/go-azure-sdk/resource-manager/hybridcompute/2022-11-10/machines" + "github.com/hashicorp/go-azure-sdk/resource-manager/systemcentervirtualmachinemanager/2023-10-07/availabilitysets" + "github.com/hashicorp/go-azure-sdk/resource-manager/systemcentervirtualmachinemanager/2023-10-07/clouds" + "github.com/hashicorp/go-azure-sdk/resource-manager/systemcentervirtualmachinemanager/2023-10-07/inventoryitems" + "github.com/hashicorp/go-azure-sdk/resource-manager/systemcentervirtualmachinemanager/2023-10-07/virtualmachineinstances" + "github.com/hashicorp/go-azure-sdk/resource-manager/systemcentervirtualmachinemanager/2023-10-07/virtualmachinetemplates" + "github.com/hashicorp/go-azure-sdk/resource-manager/systemcentervirtualmachinemanager/2023-10-07/virtualnetworks" + "github.com/hashicorp/go-azure-sdk/resource-manager/systemcentervirtualmachinemanager/2023-10-07/vmmservers" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + networkValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/network/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/systemcentervirtualmachinemanager/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/systemcentervirtualmachinemanager/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" +) + +type SystemCenterVirtualMachineManagerVirtualMachineInstanceModel struct { + ScopedResourceId string `tfschema:"scoped_resource_id"` + CustomLocationId string `tfschema:"custom_location_id"` + Hardware []Hardware `tfschema:"hardware"` + Infrastructure []Infrastructure `tfschema:"infrastructure"` + NetworkInterfaces []NetworkInterface `tfschema:"network_interface"` + OperatingSystem []OperatingSystem `tfschema:"operating_system"` + StorageDisks []StorageDisk `tfschema:"storage_disk"` + SystemCenterVirtualMachineManagerAvailabilitySetIds []string `tfschema:"system_center_virtual_machine_manager_availability_set_ids"` +} + +type Hardware struct { + CpuCount int64 `tfschema:"cpu_count"` + DynamicMemoryMaxInMb int64 `tfschema:"dynamic_memory_max_in_mb"` + DynamicMemoryMinInMb int64 `tfschema:"dynamic_memory_min_in_mb"` + LimitCpuForMigrationEnabled bool `tfschema:"limit_cpu_for_migration_enabled"` + MemoryInMb int64 `tfschema:"memory_in_mb"` +} + +type Infrastructure struct { + CheckpointType string `tfschema:"checkpoint_type"` + SystemCenterVirtualMachineManagerCloudId string `tfschema:"system_center_virtual_machine_manager_cloud_id"` + SystemCenterVirtualMachineManagerInventoryItemId string `tfschema:"system_center_virtual_machine_manager_inventory_item_id"` + SystemCenterVirtualMachineManagerTemplateId string `tfschema:"system_center_virtual_machine_manager_template_id"` + SystemCenterVirtualMachineManagerVirtualMachineServerId string `tfschema:"system_center_virtual_machine_manager_virtual_machine_server_id"` +} + +type NetworkInterface struct { + Name string `tfschema:"name"` + Ipv4AddressType string `tfschema:"ipv4_address_type"` + Ipv6AddressType string `tfschema:"ipv6_address_type"` + MacAddressType string `tfschema:"mac_address_type"` + VirtualNetworkId string `tfschema:"virtual_network_id"` +} + +type OperatingSystem struct { + ComputerName string `tfschema:"computer_name"` + AdminPassword string `tfschema:"admin_password"` +} + +type StorageDisk struct { + Bus int64 `tfschema:"bus"` + BusType string `tfschema:"bus_type"` + DiskSizeGB int64 `tfschema:"disk_size_gb"` + Lun int64 `tfschema:"lun"` + Name string `tfschema:"name"` + StorageQoSPolicyName string `tfschema:"storage_qos_policy_name"` + TemplateDiskId string `tfschema:"template_disk_id"` + VhdType string `tfschema:"vhd_type"` +} + +var ( + _ sdk.Resource = SystemCenterVirtualMachineManagerVirtualMachineInstanceResource{} + _ sdk.ResourceWithUpdate = SystemCenterVirtualMachineManagerVirtualMachineInstanceResource{} +) + +type SystemCenterVirtualMachineManagerVirtualMachineInstanceResource struct{} + +func (r SystemCenterVirtualMachineManagerVirtualMachineInstanceResource) ModelObject() interface{} { + return &SystemCenterVirtualMachineManagerVirtualMachineInstanceModel{} +} + +func (r SystemCenterVirtualMachineManagerVirtualMachineInstanceResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return validate.SystemCenterVirtualMachineManagerVirtualMachineInstanceID +} + +func (r SystemCenterVirtualMachineManagerVirtualMachineInstanceResource) ResourceType() string { + return "azurerm_system_center_virtual_machine_manager_virtual_machine_instance" +} + +func (r SystemCenterVirtualMachineManagerVirtualMachineInstanceResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "scoped_resource_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: machines.ValidateMachineID, + }, + + "custom_location_id": commonschema.ResourceIDReferenceRequiredForceNew(&customlocations.CustomLocationId{}), + + "infrastructure": { + Type: pluginsdk.TypeList, + Required: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "checkpoint_type": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "Disabled", + "Production", + "ProductionOnly", + "Standard", + }, false), + }, + + "system_center_virtual_machine_manager_cloud_id": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: clouds.ValidateCloudID, + RequiredWith: []string{"infrastructure.0.system_center_virtual_machine_manager_template_id"}, + AtLeastOneOf: []string{"infrastructure.0.system_center_virtual_machine_manager_cloud_id", "infrastructure.0.system_center_virtual_machine_manager_inventory_item_id", "infrastructure.0.system_center_virtual_machine_manager_template_id", "infrastructure.0.system_center_virtual_machine_manager_virtual_machine_server_id"}, + }, + + "system_center_virtual_machine_manager_inventory_item_id": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: inventoryitems.ValidateInventoryItemID, + RequiredWith: []string{"infrastructure.0.system_center_virtual_machine_manager_virtual_machine_server_id"}, + AtLeastOneOf: []string{"infrastructure.0.system_center_virtual_machine_manager_cloud_id", "infrastructure.0.system_center_virtual_machine_manager_inventory_item_id", "infrastructure.0.system_center_virtual_machine_manager_template_id", "infrastructure.0.system_center_virtual_machine_manager_virtual_machine_server_id"}, + }, + + "system_center_virtual_machine_manager_template_id": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: virtualmachinetemplates.ValidateVirtualMachineTemplateID, + RequiredWith: []string{"infrastructure.0.system_center_virtual_machine_manager_cloud_id"}, + AtLeastOneOf: []string{"infrastructure.0.system_center_virtual_machine_manager_cloud_id", "infrastructure.0.system_center_virtual_machine_manager_inventory_item_id", "infrastructure.0.system_center_virtual_machine_manager_template_id", "infrastructure.0.system_center_virtual_machine_manager_virtual_machine_server_id"}, + }, + + "system_center_virtual_machine_manager_virtual_machine_server_id": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: vmmservers.ValidateVMmServerID, + AtLeastOneOf: []string{"infrastructure.0.system_center_virtual_machine_manager_cloud_id", "infrastructure.0.system_center_virtual_machine_manager_inventory_item_id", "infrastructure.0.system_center_virtual_machine_manager_template_id", "infrastructure.0.system_center_virtual_machine_manager_virtual_machine_server_id"}, + }, + }, + }, + }, + + "hardware": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "cpu_count": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(1, 64), + AtLeastOneOf: []string{"hardware.0.cpu_count", "hardware.0.dynamic_memory_max_in_mb", "hardware.0.dynamic_memory_min_in_mb", "hardware.0.limit_cpu_for_migration_enabled", "hardware.0.memory_in_mb"}, + }, + + "dynamic_memory_max_in_mb": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(32, 1048576), + AtLeastOneOf: []string{"hardware.0.cpu_count", "hardware.0.dynamic_memory_max_in_mb", "hardware.0.dynamic_memory_min_in_mb", "hardware.0.limit_cpu_for_migration_enabled", "hardware.0.memory_in_mb"}, + }, + + "dynamic_memory_min_in_mb": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(32, 1048576), + AtLeastOneOf: []string{"hardware.0.cpu_count", "hardware.0.dynamic_memory_max_in_mb", "hardware.0.dynamic_memory_min_in_mb", "hardware.0.limit_cpu_for_migration_enabled", "hardware.0.memory_in_mb"}, + }, + + "limit_cpu_for_migration_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + AtLeastOneOf: []string{"hardware.0.cpu_count", "hardware.0.dynamic_memory_max_in_mb", "hardware.0.dynamic_memory_min_in_mb", "hardware.0.limit_cpu_for_migration_enabled", "hardware.0.memory_in_mb"}, + }, + + "memory_in_mb": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(32, 1048576), + AtLeastOneOf: []string{"hardware.0.cpu_count", "hardware.0.dynamic_memory_max_in_mb", "hardware.0.dynamic_memory_min_in_mb", "hardware.0.limit_cpu_for_migration_enabled", "hardware.0.memory_in_mb"}, + }, + }, + }, + }, + + "network_interface": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: networkValidate.NetworkInterfaceName, + }, + + "virtual_network_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: virtualnetworks.ValidateVirtualNetworkID, + }, + + "ipv4_address_type": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(virtualmachineinstances.PossibleValuesForAllocationMethod(), false), + }, + + "ipv6_address_type": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(virtualmachineinstances.PossibleValuesForAllocationMethod(), false), + }, + + "mac_address_type": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(virtualmachineinstances.PossibleValuesForAllocationMethod(), false), + }, + }, + }, + }, + + "operating_system": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "computer_name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.SystemCenterVirtualMachineManagerVirtualMachineInstanceComputerName, + }, + + "admin_password": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + Sensitive: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "storage_disk": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "bus": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 3), + }, + + "bus_type": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "IDE", + "SCSI", + }, false), + }, + + "disk_size_gb": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(1), + }, + + "lun": { + Type: pluginsdk.TypeInt, + Optional: true, + ValidateFunc: validation.IntBetween(0, 63), + }, + + "name": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validate.SystemCenterVirtualMachineManagerVirtualMachineInstanceStorageDiskName, + }, + + "storage_qos_policy_name": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "template_disk_id": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IsUUID, + }, + + "vhd_type": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + "Dynamic", + "Fixed", + }, false), + }, + }, + }, + }, + + "system_center_virtual_machine_manager_availability_set_ids": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: availabilitysets.ValidateAvailabilitySetID, + }, + }, + } +} + +func (r SystemCenterVirtualMachineManagerVirtualMachineInstanceResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (r SystemCenterVirtualMachineManagerVirtualMachineInstanceResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 60 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.SystemCenterVirtualMachineManager.VirtualMachineInstances + + var model SystemCenterVirtualMachineManagerVirtualMachineInstanceModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + id := parse.NewSystemCenterVirtualMachineManagerVirtualMachineInstanceID(model.ScopedResourceId) + + existing, err := client.Get(ctx, commonids.NewScopeID(id.Scope)) + if err != nil { + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for the presence of an existing %s: %+v", id, err) + } + } + if !response.WasNotFound(existing.HttpResponse) { + return metadata.ResourceRequiresImport(r.ResourceType(), id) + } + + parameters := virtualmachineinstances.VirtualMachineInstance{ + ExtendedLocation: virtualmachineinstances.ExtendedLocation{ + Type: pointer.To("customLocation"), + Name: pointer.To(model.CustomLocationId), + }, + Properties: &virtualmachineinstances.VirtualMachineInstanceProperties{ + InfrastructureProfile: expandSystemCenterVirtualMachineManagerVirtualMachineInstanceInfrastructureProfileForCreate(model.Infrastructure), + HardwareProfile: expandSystemCenterVirtualMachineManagerVirtualMachineInstanceHardwareProfileForCreate(model.Hardware, metadata.ResourceData), + OsProfile: expandSystemCenterVirtualMachineManagerVirtualMachineInstanceOSProfile(model.OperatingSystem), + }, + } + + if v := model.NetworkInterfaces; v != nil { + parameters.Properties.NetworkProfile = &virtualmachineinstances.NetworkProfile{ + NetworkInterfaces: expandSystemCenterVirtualMachineManagerVirtualMachineInstanceNetworkInterfacesForCreate(v), + } + } + + if v := model.StorageDisks; v != nil { + parameters.Properties.StorageProfile = &virtualmachineinstances.StorageProfile{ + Disks: expandSystemCenterVirtualMachineManagerVirtualMachineInstanceStorageDisksForCreate(v, metadata.ResourceData), + } + } + + if v := model.SystemCenterVirtualMachineManagerAvailabilitySetIds; v != nil { + availabilitySets, err := expandSystemCenterVirtualMachineManagerVirtualMachineInstanceAvailabilitySets(v) + if err != nil { + return err + } + parameters.Properties.AvailabilitySets = availabilitySets + } + + if err := client.CreateOrUpdateThenPoll(ctx, commonids.NewScopeID(id.Scope), parameters); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + return nil + }, + } +} + +func (r SystemCenterVirtualMachineManagerVirtualMachineInstanceResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.SystemCenterVirtualMachineManager.VirtualMachineInstances + + id, err := parse.SystemCenterVirtualMachineManagerVirtualMachineInstanceID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, commonids.NewScopeID(id.Scope)) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(*id) + } + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + state := SystemCenterVirtualMachineManagerVirtualMachineInstanceModel{ + ScopedResourceId: id.Scope, + } + + if model := resp.Model; model != nil { + state.CustomLocationId = pointer.From(model.ExtendedLocation.Name) + + if props := model.Properties; props != nil { + state.Hardware = flattenSystemCenterVirtualMachineManagerVirtualMachineInstanceHardwareProfile(props.HardwareProfile) + state.Infrastructure = flattenSystemCenterVirtualMachineManagerVirtualMachineInstanceInfrastructureProfile(props.InfrastructureProfile) + state.OperatingSystem = flattenSystemCenterVirtualMachineManagerVirtualMachineInstanceOSProfile(props.OsProfile, metadata.ResourceData.Get("operating_system.0.admin_password").(string)) + state.SystemCenterVirtualMachineManagerAvailabilitySetIds = flattenSystemCenterVirtualMachineManagerVirtualMachineInstanceAvailabilitySets(props.AvailabilitySets) + + if v := props.NetworkProfile; v != nil { + state.NetworkInterfaces = flattenSystemCenterVirtualMachineManagerVirtualMachineInstanceNetworkInterfaces(v.NetworkInterfaces) + } + + if v := props.StorageProfile; v != nil { + state.StorageDisks = flattenSystemCenterVirtualMachineManagerVirtualMachineInstanceStorageDisks(v.Disks) + } + } + } + + return metadata.Encode(&state) + }, + } +} + +func (r SystemCenterVirtualMachineManagerVirtualMachineInstanceResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 60 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.SystemCenterVirtualMachineManager.VirtualMachineInstances + + id, err := parse.SystemCenterVirtualMachineManagerVirtualMachineInstanceID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + var model SystemCenterVirtualMachineManagerVirtualMachineInstanceModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + parameters := virtualmachineinstances.VirtualMachineInstanceUpdate{ + Properties: &virtualmachineinstances.VirtualMachineInstanceUpdateProperties{}, + } + + if metadata.ResourceData.HasChange("infrastructure") { + parameters.Properties.InfrastructureProfile = expandSystemCenterVirtualMachineManagerVirtualMachineInstanceInfrastructureProfileForUpdate(model.Infrastructure) + } + + if metadata.ResourceData.HasChange("hardware") { + parameters.Properties.HardwareProfile = expandSystemCenterVirtualMachineManagerVirtualMachineInstanceHardwareProfileForUpdate(model.Hardware, metadata.ResourceData) + } + + if metadata.ResourceData.HasChange("network_interface") { + parameters.Properties.NetworkProfile = &virtualmachineinstances.NetworkProfileUpdate{ + NetworkInterfaces: expandSystemCenterVirtualMachineManagerVirtualMachineInstanceNetworkInterfacesForUpdate(model.NetworkInterfaces), + } + } + + if metadata.ResourceData.HasChange("storage_disk") { + parameters.Properties.StorageProfile = &virtualmachineinstances.StorageProfileUpdate{ + Disks: expandSystemCenterVirtualMachineManagerVirtualMachineInstanceStorageDisksForUpdate(model.StorageDisks, metadata.ResourceData), + } + } + + if metadata.ResourceData.HasChange("system_center_virtual_machine_manager_availability_set_ids") { + availabilitySets, err := expandSystemCenterVirtualMachineManagerVirtualMachineInstanceAvailabilitySets(model.SystemCenterVirtualMachineManagerAvailabilitySetIds) + if err != nil { + return err + } + parameters.Properties.AvailabilitySets = availabilitySets + } + + needToRestart := metadata.ResourceData.HasChange("hardware") || metadata.ResourceData.HasChange("network_interface") || metadata.ResourceData.HasChange("storage_disk") + + if needToRestart { + if err := client.StopThenPoll(ctx, commonids.NewScopeID(id.Scope), virtualmachineinstances.StopVirtualMachineOptions{ + SkipShutdown: pointer.To(virtualmachineinstances.SkipShutdownFalse), + }); err != nil { + return fmt.Errorf("stopping %s: %+v", *id, err) + } + } + + if err := client.UpdateThenPoll(ctx, commonids.NewScopeID(id.Scope), parameters); err != nil { + return fmt.Errorf("updating %s: %+v", *id, err) + } + + if needToRestart { + if err := client.StartThenPoll(ctx, commonids.NewScopeID(id.Scope)); err != nil { + return fmt.Errorf("starting %s: %+v", *id, err) + } + } + + return nil + }, + } +} + +func (r SystemCenterVirtualMachineManagerVirtualMachineInstanceResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 60 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.SystemCenterVirtualMachineManager.VirtualMachineInstances + + id, err := parse.SystemCenterVirtualMachineManagerVirtualMachineInstanceID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + if err := client.DeleteThenPoll(ctx, commonids.NewScopeID(id.Scope), virtualmachineinstances.DeleteOperationOptions{ + DeleteFromHost: pointer.To(virtualmachineinstances.DeleteFromHostTrue), + Force: pointer.To(virtualmachineinstances.ForceDeleteTrue), + }); err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + + return nil + }, + } +} + +func expandSystemCenterVirtualMachineManagerVirtualMachineInstanceInfrastructureProfileForCreate(input []Infrastructure) *virtualmachineinstances.InfrastructureProfile { + if len(input) == 0 { + return nil + } + + infrastructureProfile := input[0] + + result := virtualmachineinstances.InfrastructureProfile{} + + if v := infrastructureProfile.CheckpointType; v != "" { + result.CheckpointType = pointer.To(v) + } + + if v := infrastructureProfile.SystemCenterVirtualMachineManagerCloudId; v != "" { + result.CloudId = pointer.To(v) + } + + if v := infrastructureProfile.SystemCenterVirtualMachineManagerInventoryItemId; v != "" { + result.InventoryItemId = pointer.To(v) + } + + if v := infrastructureProfile.SystemCenterVirtualMachineManagerTemplateId; v != "" { + result.TemplateId = pointer.To(v) + } + + if v := infrastructureProfile.SystemCenterVirtualMachineManagerVirtualMachineServerId; v != "" { + result.VMmServerId = pointer.To(v) + } + + return &result +} + +func expandSystemCenterVirtualMachineManagerVirtualMachineInstanceHardwareProfileForCreate(input []Hardware, d *pluginsdk.ResourceData) *virtualmachineinstances.HardwareProfile { + if len(input) == 0 { + return nil + } + + hardwareProfile := input[0] + + result := virtualmachineinstances.HardwareProfile{} + + // As TF always sets bool value to false when it isn't set, so it has to use d.GetRawConfig() to determine whether it is set in the tf config + if v := d.GetRawConfig().AsValueMap()["hardware"].AsValueSlice()[0].AsValueMap()["limit_cpu_for_migration_enabled"]; !v.IsNull() { + result.LimitCPUForMigration = pointer.To(virtualmachineinstances.LimitCPUForMigration(strconv.FormatBool(hardwareProfile.LimitCpuForMigrationEnabled))) + } + + if v := hardwareProfile.CpuCount; v != 0 { + result.CpuCount = pointer.To(v) + } + + dynamicMemoryEnabled := false + if hardwareProfile.DynamicMemoryMaxInMb != 0 || hardwareProfile.DynamicMemoryMinInMb != 0 { + dynamicMemoryEnabled = true + } + result.DynamicMemoryEnabled = pointer.To(virtualmachineinstances.DynamicMemoryEnabled(strconv.FormatBool(dynamicMemoryEnabled))) + + if v := hardwareProfile.DynamicMemoryMaxInMb; v != 0 { + result.DynamicMemoryMaxMB = pointer.To(v) + } + + if v := hardwareProfile.DynamicMemoryMinInMb; v != 0 { + result.DynamicMemoryMinMB = pointer.To(v) + } + + if v := hardwareProfile.MemoryInMb; v != 0 { + result.MemoryMB = pointer.To(v) + } + + return &result +} + +func expandSystemCenterVirtualMachineManagerVirtualMachineInstanceNetworkInterfacesForCreate(input []NetworkInterface) *[]virtualmachineinstances.NetworkInterface { + result := make([]virtualmachineinstances.NetworkInterface, 0) + if len(input) == 0 { + return &result + } + + for _, v := range input { + networkInterface := virtualmachineinstances.NetworkInterface{ + Name: pointer.To(v.Name), + } + + if ipv4AddressType := v.Ipv4AddressType; ipv4AddressType != "" { + networkInterface.IPv4AddressType = pointer.To(virtualmachineinstances.AllocationMethod(ipv4AddressType)) + } + + if ipv6AddressType := v.Ipv6AddressType; ipv6AddressType != "" { + networkInterface.IPv6AddressType = pointer.To(virtualmachineinstances.AllocationMethod(ipv6AddressType)) + } + + if macAddressType := v.MacAddressType; macAddressType != "" { + networkInterface.MacAddressType = pointer.To(virtualmachineinstances.AllocationMethod(macAddressType)) + } + + if vnetId := v.VirtualNetworkId; vnetId != "" { + networkInterface.VirtualNetworkId = pointer.To(vnetId) + } + + result = append(result, networkInterface) + } + + return &result +} + +func expandSystemCenterVirtualMachineManagerVirtualMachineInstanceOSProfile(input []OperatingSystem) *virtualmachineinstances.OsProfileForVMInstance { + if len(input) == 0 { + return nil + } + + osProfile := input[0] + + result := virtualmachineinstances.OsProfileForVMInstance{ + ComputerName: pointer.To(osProfile.ComputerName), + } + + if v := osProfile.AdminPassword; v != "" { + result.AdminPassword = pointer.To(v) + } + + return &result +} + +func expandSystemCenterVirtualMachineManagerVirtualMachineInstanceStorageDisksForCreate(input []StorageDisk, d *pluginsdk.ResourceData) *[]virtualmachineinstances.VirtualDisk { + result := make([]virtualmachineinstances.VirtualDisk, 0) + if len(input) == 0 { + return &result + } + + for k, v := range input { + virtualDisk := virtualmachineinstances.VirtualDisk{} + + // As API allows zero value for this property, so TF has to use d.GetRawConfig() to determine whether it's set in tf config + if bus := d.GetRawConfig().AsValueMap()["storage_disk"].AsValueSlice()[k].AsValueMap()["bus"]; !bus.IsNull() { + virtualDisk.Bus = pointer.To(v.Bus) + } + + if lun := d.GetRawConfig().AsValueMap()["storage_disk"].AsValueSlice()[k].AsValueMap()["lun"]; !lun.IsNull() { + virtualDisk.Lun = pointer.To(v.Lun) + } + + if busType := v.BusType; busType != "" { + virtualDisk.BusType = pointer.To(busType) + } + + if diskSizeGB := v.DiskSizeGB; diskSizeGB != 0 { + virtualDisk.DiskSizeGB = pointer.To(diskSizeGB) + } + + if name := v.Name; name != "" { + virtualDisk.Name = pointer.To(name) + } + + if templateDiskId := v.TemplateDiskId; templateDiskId != "" { + virtualDisk.TemplateDiskId = pointer.To(templateDiskId) + } + + if vhdType := v.VhdType; vhdType != "" { + virtualDisk.VhdType = pointer.To(vhdType) + } + + if storageQosPolicyName := v.StorageQoSPolicyName; storageQosPolicyName != "" { + virtualDisk.StorageQoSPolicy = &virtualmachineinstances.StorageQosPolicyDetails{ + Name: pointer.To(storageQosPolicyName), + } + } + + result = append(result, virtualDisk) + } + + return &result +} + +func expandSystemCenterVirtualMachineManagerVirtualMachineInstanceAvailabilitySets(input []string) (*[]virtualmachineinstances.AvailabilitySetListItem, error) { + result := make([]virtualmachineinstances.AvailabilitySetListItem, 0) + if len(input) == 0 { + return &result, nil + } + + for _, v := range input { + availabilitySetId, err := availabilitysets.ParseAvailabilitySetID(v) + if err != nil { + return nil, err + } + + result = append(result, virtualmachineinstances.AvailabilitySetListItem{ + Id: pointer.To(availabilitySetId.ID()), + Name: pointer.To(availabilitySetId.AvailabilitySetName), + }) + } + + return &result, nil +} + +func expandSystemCenterVirtualMachineManagerVirtualMachineInstanceInfrastructureProfileForUpdate(input []Infrastructure) *virtualmachineinstances.InfrastructureProfileUpdate { + if len(input) == 0 { + return nil + } + + infrastructureProfile := input[0] + + result := virtualmachineinstances.InfrastructureProfileUpdate{} + + if v := infrastructureProfile.CheckpointType; v != "" { + result.CheckpointType = pointer.To(v) + } + + return &result +} + +func expandSystemCenterVirtualMachineManagerVirtualMachineInstanceHardwareProfileForUpdate(input []Hardware, d *pluginsdk.ResourceData) *virtualmachineinstances.HardwareProfileUpdate { + result := virtualmachineinstances.HardwareProfileUpdate{} + + if len(input) == 0 { + return &result + } + + hardwareProfile := input[0] + + // As TF always sets bool value to false when it isn't set, so it has to use d.GetRawConfig() to determine whether it is set in the tf config + if v := d.GetRawConfig().AsValueMap()["hardware"].AsValueSlice()[0].AsValueMap()["limit_cpu_for_migration_enabled"]; !v.IsNull() { + result.LimitCPUForMigration = pointer.To(virtualmachineinstances.LimitCPUForMigration(strconv.FormatBool(hardwareProfile.LimitCpuForMigrationEnabled))) + } + + if v := hardwareProfile.CpuCount; v != 0 { + result.CpuCount = pointer.To(v) + } + + dynamicMemoryEnabled := false + if hardwareProfile.DynamicMemoryMaxInMb != 0 || hardwareProfile.DynamicMemoryMinInMb != 0 { + dynamicMemoryEnabled = true + } + result.DynamicMemoryEnabled = pointer.To(virtualmachineinstances.DynamicMemoryEnabled(strconv.FormatBool(dynamicMemoryEnabled))) + + if v := hardwareProfile.DynamicMemoryMaxInMb; v != 0 { + result.DynamicMemoryMaxMB = pointer.To(v) + } + + if v := hardwareProfile.DynamicMemoryMinInMb; v != 0 { + result.DynamicMemoryMinMB = pointer.To(v) + } + + if v := hardwareProfile.MemoryInMb; v != 0 { + result.MemoryMB = pointer.To(v) + } + + return &result +} + +func expandSystemCenterVirtualMachineManagerVirtualMachineInstanceNetworkInterfacesForUpdate(input []NetworkInterface) *[]virtualmachineinstances.NetworkInterfaceUpdate { + result := make([]virtualmachineinstances.NetworkInterfaceUpdate, 0) + if len(input) == 0 { + return &result + } + + for _, v := range input { + networkInterface := virtualmachineinstances.NetworkInterfaceUpdate{ + Name: pointer.To(v.Name), + } + + if ipv4AddressType := v.Ipv4AddressType; ipv4AddressType != "" { + networkInterface.IPv4AddressType = pointer.To(virtualmachineinstances.AllocationMethod(ipv4AddressType)) + } + + if ipv6AddressType := v.Ipv6AddressType; ipv6AddressType != "" { + networkInterface.IPv6AddressType = pointer.To(virtualmachineinstances.AllocationMethod(ipv6AddressType)) + } + + if macAddressType := v.MacAddressType; macAddressType != "" { + networkInterface.MacAddressType = pointer.To(virtualmachineinstances.AllocationMethod(macAddressType)) + } + + if vnetId := v.VirtualNetworkId; vnetId != "" { + networkInterface.VirtualNetworkId = pointer.To(vnetId) + } + + result = append(result, networkInterface) + } + + return &result +} + +func expandSystemCenterVirtualMachineManagerVirtualMachineInstanceStorageDisksForUpdate(input []StorageDisk, d *pluginsdk.ResourceData) *[]virtualmachineinstances.VirtualDiskUpdate { + result := make([]virtualmachineinstances.VirtualDiskUpdate, 0) + if len(input) == 0 { + return &result + } + + for k, v := range input { + virtualDisk := virtualmachineinstances.VirtualDiskUpdate{} + + // As API allows zero value for this property, so TF has to use d.GetRawConfig() to determine whether it's set in tf config + if bus := d.GetRawConfig().AsValueMap()["storage_disk"].AsValueSlice()[k].AsValueMap()["bus"]; !bus.IsNull() { + virtualDisk.Bus = pointer.To(v.Bus) + } + + if lun := d.GetRawConfig().AsValueMap()["storage_disk"].AsValueSlice()[k].AsValueMap()["lun"]; !lun.IsNull() { + virtualDisk.Lun = pointer.To(v.Lun) + } + + if busType := v.BusType; busType != "" { + virtualDisk.BusType = pointer.To(busType) + } + + if diskSizeGB := v.DiskSizeGB; diskSizeGB != 0 { + virtualDisk.DiskSizeGB = pointer.To(diskSizeGB) + } + + if name := v.Name; name != "" { + virtualDisk.Name = pointer.To(name) + } + + if vhdType := v.VhdType; vhdType != "" { + virtualDisk.VhdType = pointer.To(vhdType) + } + + if storageQosPolicyName := v.StorageQoSPolicyName; storageQosPolicyName != "" { + virtualDisk.StorageQoSPolicy = &virtualmachineinstances.StorageQosPolicyDetails{ + Name: pointer.To(storageQosPolicyName), + } + } + + result = append(result, virtualDisk) + } + + return &result +} + +func flattenSystemCenterVirtualMachineManagerVirtualMachineInstanceInfrastructureProfile(input *virtualmachineinstances.InfrastructureProfile) []Infrastructure { + result := make([]Infrastructure, 0) + if input == nil { + return result + } + + return append(result, Infrastructure{ + CheckpointType: pointer.From(input.CheckpointType), + SystemCenterVirtualMachineManagerCloudId: pointer.From(input.CloudId), + SystemCenterVirtualMachineManagerInventoryItemId: pointer.From(input.InventoryItemId), + SystemCenterVirtualMachineManagerTemplateId: pointer.From(input.TemplateId), + SystemCenterVirtualMachineManagerVirtualMachineServerId: pointer.From(input.VMmServerId), + }) +} + +func flattenSystemCenterVirtualMachineManagerVirtualMachineInstanceHardwareProfile(input *virtualmachineinstances.HardwareProfile) []Hardware { + result := make([]Hardware, 0) + if input == nil { + return result + } + + return append(result, Hardware{ + CpuCount: pointer.From(input.CpuCount), + DynamicMemoryMaxInMb: pointer.From(input.DynamicMemoryMaxMB), + DynamicMemoryMinInMb: pointer.From(input.DynamicMemoryMinMB), + LimitCpuForMigrationEnabled: pointer.From(input.LimitCPUForMigration) == virtualmachineinstances.LimitCPUForMigrationTrue, + MemoryInMb: pointer.From(input.MemoryMB), + }) +} + +func flattenSystemCenterVirtualMachineManagerVirtualMachineInstanceNetworkInterfaces(input *[]virtualmachineinstances.NetworkInterface) []NetworkInterface { + result := make([]NetworkInterface, 0) + if input == nil { + return result + } + + for _, v := range *input { + result = append(result, NetworkInterface{ + Name: pointer.From(v.Name), + VirtualNetworkId: pointer.From(v.VirtualNetworkId), + Ipv4AddressType: string(pointer.From(v.IPv4AddressType)), + Ipv6AddressType: string(pointer.From(v.IPv6AddressType)), + MacAddressType: string(pointer.From(v.MacAddressType)), + }) + } + + return result +} + +func flattenSystemCenterVirtualMachineManagerVirtualMachineInstanceOSProfile(input *virtualmachineinstances.OsProfileForVMInstance, adminPassword string) []OperatingSystem { + result := make([]OperatingSystem, 0) + if input == nil { + return result + } + + return append(result, OperatingSystem{ + ComputerName: pointer.From(input.ComputerName), + AdminPassword: adminPassword, + }) +} + +func flattenSystemCenterVirtualMachineManagerVirtualMachineInstanceStorageDisks(input *[]virtualmachineinstances.VirtualDisk) []StorageDisk { + result := make([]StorageDisk, 0) + if input == nil { + return result + } + + for _, v := range *input { + // service team confirmed that diskSizeGB in the request payload and maxDiskSizeGB in the API response both represent the maximum size of the disk but diskSizeGB in the API response represents the actual disk size + storageDisk := StorageDisk{ + Bus: pointer.From(v.Bus), + BusType: pointer.From(v.BusType), + DiskSizeGB: pointer.From(v.MaxDiskSizeGB), + Lun: pointer.From(v.Lun), + Name: pointer.From(v.Name), + TemplateDiskId: pointer.From(v.TemplateDiskId), + VhdType: pointer.From(v.VhdType), + } + + if storageQoSPolicy := v.StorageQoSPolicy; storageQoSPolicy != nil { + storageDisk.StorageQoSPolicyName = pointer.From(storageQoSPolicy.Name) + } + + result = append(result, storageDisk) + } + + return result +} + +func flattenSystemCenterVirtualMachineManagerVirtualMachineInstanceAvailabilitySets(input *[]virtualmachineinstances.AvailabilitySetListItem) []string { + result := make([]string, 0) + if input == nil { + return result + } + + for _, v := range *input { + result = append(result, pointer.From(v.Id)) + } + + return result +} diff --git a/internal/services/systemcentervirtualmachinemanager/system_center_virtual_machine_manager_virtual_machine_instance_resource_test.go b/internal/services/systemcentervirtualmachinemanager/system_center_virtual_machine_manager_virtual_machine_instance_resource_test.go new file mode 100644 index 000000000000..e628ae35ccf5 --- /dev/null +++ b/internal/services/systemcentervirtualmachinemanager/system_center_virtual_machine_manager_virtual_machine_instance_resource_test.go @@ -0,0 +1,515 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package systemcentervirtualmachinemanager_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/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/systemcentervirtualmachinemanager/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +type SystemCenterVirtualMachineManagerVirtualMachineInstanceResource struct{} + +func TestAccSystemCenterVirtualMachineManagerVirtualMachineInstanceSequential(t *testing.T) { + // NOTE: this is a combined test rather than separate split out tests because only one System Center Virtual Machine Manager Server can be onboarded at a time on a given Custom Location + + if os.Getenv("ARM_TEST_CUSTOM_LOCATION_ID") == "" || os.Getenv("ARM_TEST_FQDN") == "" || os.Getenv("ARM_TEST_USERNAME") == "" || os.Getenv("ARM_TEST_PASSWORD") == "" || os.Getenv("ARM_TEST_VIRTUAL_NETWORK_INVENTORY_NAME") == "" { + t.Skip("Skipping as one of `ARM_TEST_CUSTOM_LOCATION_ID`, `ARM_TEST_FQDN`, `ARM_TEST_USERNAME`, `ARM_TEST_PASSWORD`, `ARM_TEST_VIRTUAL_NETWORK_INVENTORY_NAME` was not specified") + } + + acceptance.RunTestsInSequence(t, map[string]map[string]func(t *testing.T){ + "scvmmVirtualMachineInstance": { + "basic": testAccSystemCenterVirtualMachineManagerVirtualMachineInstance_basic, + "requiresImport": testAccSystemCenterVirtualMachineManagerVirtualMachineInstance_requiresImport, + "complete": testAccSystemCenterVirtualMachineManagerVirtualMachineInstance_complete, + "update": testAccSystemCenterVirtualMachineManagerVirtualMachineInstance_update, + "inventoryItemId": testAccSystemCenterVirtualMachineManagerVirtualMachineInstance_inventoryItemId, + }, + }) +} + +func testAccSystemCenterVirtualMachineManagerVirtualMachineInstance_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_system_center_virtual_machine_manager_virtual_machine_instance", "test") + r := SystemCenterVirtualMachineManagerVirtualMachineInstanceResource{} + + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func testAccSystemCenterVirtualMachineManagerVirtualMachineInstance_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_system_center_virtual_machine_manager_virtual_machine_instance", "test") + r := SystemCenterVirtualMachineManagerVirtualMachineInstanceResource{} + + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func testAccSystemCenterVirtualMachineManagerVirtualMachineInstance_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_system_center_virtual_machine_manager_virtual_machine_instance", "test") + r := SystemCenterVirtualMachineManagerVirtualMachineInstanceResource{} + + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("operating_system.0.admin_password"), + }) +} + +func testAccSystemCenterVirtualMachineManagerVirtualMachineInstance_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_system_center_virtual_machine_manager_virtual_machine_instance", "test") + r := SystemCenterVirtualMachineManagerVirtualMachineInstanceResource{} + + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("operating_system.0.admin_password"), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("operating_system.0.admin_password"), + }) +} + +func testAccSystemCenterVirtualMachineManagerVirtualMachineInstance_inventoryItemId(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_system_center_virtual_machine_manager_virtual_machine_instance", "test") + r := SystemCenterVirtualMachineManagerVirtualMachineInstanceResource{} + + data.ResourceSequentialTest(t, r, []acceptance.TestStep{ + { + Config: r.inventoryItemId(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r SystemCenterVirtualMachineManagerVirtualMachineInstanceResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.SystemCenterVirtualMachineManagerVirtualMachineInstanceID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.SystemCenterVirtualMachineManager.VirtualMachineInstances.Get(ctx, commonids.NewScopeID(id.Scope)) + if err != nil { + return nil, fmt.Errorf("reading %s: %+v", *id, err) + } + + return pointer.To(resp.Model != nil), nil +} + +func (r SystemCenterVirtualMachineManagerVirtualMachineInstanceResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +provider "azurerm" { + features {} +} + +data "azurerm_system_center_virtual_machine_manager_inventory_items" "test" { + inventory_type = "Cloud" + system_center_virtual_machine_manager_server_id = azurerm_system_center_virtual_machine_manager_server.test.id +} + +resource "azurerm_system_center_virtual_machine_manager_cloud" "test" { + name = "acctest-scvmmc-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + custom_location_id = azurerm_system_center_virtual_machine_manager_server.test.custom_location_id + system_center_virtual_machine_manager_server_inventory_item_id = data.azurerm_system_center_virtual_machine_manager_inventory_items.test.inventory_items[0].id +} + +data "azurerm_system_center_virtual_machine_manager_inventory_items" "test2" { + inventory_type = "VirtualMachineTemplate" + system_center_virtual_machine_manager_server_id = azurerm_system_center_virtual_machine_manager_server.test.id +} + +resource "azurerm_system_center_virtual_machine_manager_virtual_machine_template" "test" { + name = "acctest-scvmmvmt-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + custom_location_id = azurerm_system_center_virtual_machine_manager_server.test.custom_location_id + system_center_virtual_machine_manager_server_inventory_item_id = data.azurerm_system_center_virtual_machine_manager_inventory_items.test2.inventory_items[0].id +} + +resource "azurerm_system_center_virtual_machine_manager_virtual_machine_instance" "test" { + scoped_resource_id = azurerm_arc_machine.test.id + custom_location_id = azurerm_system_center_virtual_machine_manager_server.test.custom_location_id + + infrastructure { + checkpoint_type = "Standard" + system_center_virtual_machine_manager_cloud_id = azurerm_system_center_virtual_machine_manager_cloud.test.id + system_center_virtual_machine_manager_template_id = azurerm_system_center_virtual_machine_manager_virtual_machine_template.test.id + system_center_virtual_machine_manager_virtual_machine_server_id = azurerm_system_center_virtual_machine_manager_server.test.id + } + + operating_system { + computer_name = "testComputer" + } + + hardware { + cpu_count = 1 + memory_in_mb = 1024 + } + + network_interface { + name = "testNIC" + ipv4_address_type = "Dynamic" + ipv6_address_type = "Dynamic" + mac_address_type = "Dynamic" + } + + lifecycle { + // Service API always provisions a virtual disk with bus type IDE per Virtual Machine Template by default + ignore_changes = [storage_disk] + } +} +`, r.template(data), data.RandomInteger, data.RandomInteger) +} + +func (r SystemCenterVirtualMachineManagerVirtualMachineInstanceResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_system_center_virtual_machine_manager_virtual_machine_instance" "import" { + scoped_resource_id = azurerm_system_center_virtual_machine_manager_virtual_machine_instance.test.scoped_resource_id + custom_location_id = azurerm_system_center_virtual_machine_manager_virtual_machine_instance.test.custom_location_id + + infrastructure { + checkpoint_type = "Standard" + system_center_virtual_machine_manager_cloud_id = azurerm_system_center_virtual_machine_manager_cloud.test.id + system_center_virtual_machine_manager_template_id = azurerm_system_center_virtual_machine_manager_virtual_machine_template.test.id + system_center_virtual_machine_manager_virtual_machine_server_id = azurerm_system_center_virtual_machine_manager_server.test.id + } + + operating_system { + computer_name = "testComputer" + } + + hardware { + cpu_count = 1 + memory_in_mb = 1024 + } + + network_interface { + name = "testNIC" + ipv4_address_type = "Dynamic" + ipv6_address_type = "Dynamic" + mac_address_type = "Dynamic" + } +} +`, r.basic(data)) +} + +func (r SystemCenterVirtualMachineManagerVirtualMachineInstanceResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +provider "azurerm" { + features {} +} + +data "azurerm_system_center_virtual_machine_manager_inventory_items" "test" { + inventory_type = "Cloud" + system_center_virtual_machine_manager_server_id = azurerm_system_center_virtual_machine_manager_server.test.id +} + +resource "azurerm_system_center_virtual_machine_manager_cloud" "test" { + name = "acctest-scvmmc-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + custom_location_id = azurerm_system_center_virtual_machine_manager_server.test.custom_location_id + system_center_virtual_machine_manager_server_inventory_item_id = data.azurerm_system_center_virtual_machine_manager_inventory_items.test.inventory_items[0].id +} + +data "azurerm_system_center_virtual_machine_manager_inventory_items" "test2" { + inventory_type = "VirtualMachineTemplate" + system_center_virtual_machine_manager_server_id = azurerm_system_center_virtual_machine_manager_server.test.id +} + +resource "azurerm_system_center_virtual_machine_manager_virtual_machine_template" "test" { + name = "acctest-scvmmvmt-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + custom_location_id = azurerm_system_center_virtual_machine_manager_server.test.custom_location_id + system_center_virtual_machine_manager_server_inventory_item_id = data.azurerm_system_center_virtual_machine_manager_inventory_items.test2.inventory_items[0].id +} + +data "azurerm_system_center_virtual_machine_manager_inventory_items" "test3" { + inventory_type = "VirtualNetwork" + system_center_virtual_machine_manager_server_id = azurerm_system_center_virtual_machine_manager_server.test.id +} + +resource "azurerm_system_center_virtual_machine_manager_virtual_network" "test" { + name = "acctest-scvmmvnet-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + custom_location_id = azurerm_system_center_virtual_machine_manager_server.test.custom_location_id + system_center_virtual_machine_manager_server_inventory_item_id = [for item in data.azurerm_system_center_virtual_machine_manager_inventory_items.test3.inventory_items : item.id if item.name == "%s"][0] +} + +resource "azurerm_system_center_virtual_machine_manager_availability_set" "test" { + name = "acctest-scvmmas-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = azurerm_system_center_virtual_machine_manager_server.test.custom_location_id + system_center_virtual_machine_manager_server_id = azurerm_system_center_virtual_machine_manager_server.test.id +} + +resource "azurerm_system_center_virtual_machine_manager_virtual_machine_instance" "test" { + scoped_resource_id = azurerm_arc_machine.test.id + custom_location_id = azurerm_system_center_virtual_machine_manager_server.test.custom_location_id + + infrastructure { + checkpoint_type = "Production" + system_center_virtual_machine_manager_cloud_id = azurerm_system_center_virtual_machine_manager_cloud.test.id + system_center_virtual_machine_manager_template_id = azurerm_system_center_virtual_machine_manager_virtual_machine_template.test.id + system_center_virtual_machine_manager_virtual_machine_server_id = azurerm_system_center_virtual_machine_manager_server.test.id + } + + operating_system { + computer_name = "testComputer" + admin_password = "AdminPassword123!" + } + + hardware { + limit_cpu_for_migration_enabled = false + cpu_count = 1 + memory_in_mb = 1024 + dynamic_memory_min_in_mb = 32 + dynamic_memory_max_in_mb = 1024 + } + + network_interface { + name = "testNIC%s" + virtual_network_id = azurerm_system_center_virtual_machine_manager_virtual_network.test.id + ipv4_address_type = "Dynamic" + ipv6_address_type = "Dynamic" + mac_address_type = "Dynamic" + } + + storage_disk { + name = "testSD%s" + bus_type = "SCSI" + vhd_type = "Dynamic" + bus = 1 + lun = 1 + disk_size_gb = 30 + } + + system_center_virtual_machine_manager_availability_set_ids = [azurerm_system_center_virtual_machine_manager_availability_set.test.id] + + lifecycle { + // Service API always provisions a virtual disk with bus type IDE per Virtual Machine Template by default, so it has to be ignored + ignore_changes = [storage_disk] + } +} +`, r.template(data), data.RandomInteger, data.RandomInteger, data.RandomInteger, os.Getenv("ARM_TEST_VIRTUAL_NETWORK_INVENTORY_NAME"), data.RandomInteger, data.RandomString, data.RandomString) +} + +func (r SystemCenterVirtualMachineManagerVirtualMachineInstanceResource) update(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +provider "azurerm" { + features {} +} + +data "azurerm_system_center_virtual_machine_manager_inventory_items" "test" { + inventory_type = "Cloud" + system_center_virtual_machine_manager_server_id = azurerm_system_center_virtual_machine_manager_server.test.id +} + +resource "azurerm_system_center_virtual_machine_manager_cloud" "test" { + name = "acctest-scvmmc-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + custom_location_id = azurerm_system_center_virtual_machine_manager_server.test.custom_location_id + system_center_virtual_machine_manager_server_inventory_item_id = data.azurerm_system_center_virtual_machine_manager_inventory_items.test.inventory_items[0].id +} + +data "azurerm_system_center_virtual_machine_manager_inventory_items" "test2" { + inventory_type = "VirtualMachineTemplate" + system_center_virtual_machine_manager_server_id = azurerm_system_center_virtual_machine_manager_server.test.id +} + +resource "azurerm_system_center_virtual_machine_manager_virtual_machine_template" "test" { + name = "acctest-scvmmvmt-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + custom_location_id = azurerm_system_center_virtual_machine_manager_server.test.custom_location_id + system_center_virtual_machine_manager_server_inventory_item_id = data.azurerm_system_center_virtual_machine_manager_inventory_items.test2.inventory_items[0].id +} + +data "azurerm_system_center_virtual_machine_manager_inventory_items" "test3" { + inventory_type = "VirtualNetwork" + system_center_virtual_machine_manager_server_id = azurerm_system_center_virtual_machine_manager_server.test.id +} + +resource "azurerm_system_center_virtual_machine_manager_virtual_network" "test" { + name = "acctest-scvmmvnet-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + custom_location_id = azurerm_system_center_virtual_machine_manager_server.test.custom_location_id + system_center_virtual_machine_manager_server_inventory_item_id = [for item in data.azurerm_system_center_virtual_machine_manager_inventory_items.test3.inventory_items : item.id if item.name == "%s"][0] +} + +resource "azurerm_system_center_virtual_machine_manager_availability_set" "test" { + name = "acctest-scvmmas-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = azurerm_system_center_virtual_machine_manager_server.test.custom_location_id + system_center_virtual_machine_manager_server_id = azurerm_system_center_virtual_machine_manager_server.test.id +} + +resource "azurerm_system_center_virtual_machine_manager_availability_set" "test2" { + name = "acctest-scvmmas2-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = azurerm_system_center_virtual_machine_manager_server.test.custom_location_id + system_center_virtual_machine_manager_server_id = azurerm_system_center_virtual_machine_manager_server.test.id +} + +resource "azurerm_system_center_virtual_machine_manager_virtual_machine_instance" "test" { + scoped_resource_id = azurerm_arc_machine.test.id + custom_location_id = azurerm_system_center_virtual_machine_manager_server.test.custom_location_id + + infrastructure { + checkpoint_type = "Standard" + system_center_virtual_machine_manager_cloud_id = azurerm_system_center_virtual_machine_manager_cloud.test.id + system_center_virtual_machine_manager_template_id = azurerm_system_center_virtual_machine_manager_virtual_machine_template.test.id + system_center_virtual_machine_manager_virtual_machine_server_id = azurerm_system_center_virtual_machine_manager_server.test.id + } + + operating_system { + computer_name = "testComputer" + admin_password = "AdminPassword123!" + } + + hardware { + limit_cpu_for_migration_enabled = false + cpu_count = 1 + memory_in_mb = 1048 + dynamic_memory_min_in_mb = 64 + dynamic_memory_max_in_mb = 1048 + } + + network_interface { + name = "testNIC2%s" + virtual_network_id = azurerm_system_center_virtual_machine_manager_virtual_network.test.id + ipv4_address_type = "Static" + ipv6_address_type = "Static" + mac_address_type = "Static" + } + + storage_disk { + name = "testSD%s" + bus_type = "SCSI" + vhd_type = "Dynamic" + bus = 1 + lun = 1 + disk_size_gb = 30 + } + + system_center_virtual_machine_manager_availability_set_ids = [azurerm_system_center_virtual_machine_manager_availability_set.test.id, azurerm_system_center_virtual_machine_manager_availability_set.test2.id] + + lifecycle { + // Service API always provisions a virtual disk with bus type IDE per Virtual Machine Template by default, so it has to be ignored + ignore_changes = [storage_disk] + } +} +`, r.template(data), data.RandomInteger, data.RandomInteger, data.RandomInteger, os.Getenv("ARM_TEST_VIRTUAL_NETWORK_INVENTORY_NAME"), data.RandomInteger, data.RandomInteger, data.RandomString, data.RandomString) +} + +func (r SystemCenterVirtualMachineManagerVirtualMachineInstanceResource) inventoryItemId(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +provider "azurerm" { + features {} +} + +data "azurerm_system_center_virtual_machine_manager_inventory_items" "test4" { + inventory_type = "VirtualMachine" + system_center_virtual_machine_manager_server_id = azurerm_system_center_virtual_machine_manager_server.test.id +} + +resource "azurerm_system_center_virtual_machine_manager_virtual_machine_instance" "test" { + scoped_resource_id = azurerm_arc_machine.test.id + custom_location_id = azurerm_system_center_virtual_machine_manager_server.test.custom_location_id + + infrastructure { + checkpoint_type = "Standard" + system_center_virtual_machine_manager_virtual_machine_server_id = azurerm_system_center_virtual_machine_manager_server.test.id + system_center_virtual_machine_manager_inventory_item_id = data.azurerm_system_center_virtual_machine_manager_inventory_items.test4.inventory_items[0].id + } + + lifecycle { + // Service API provisions VM Instance based on the existing VM Instance that includes hardware, network_interface, operating_system and storage_disk + ignore_changes = [hardware, network_interface, operating_system, storage_disk] + } +} +`, r.template(data)) +} + +func (r SystemCenterVirtualMachineManagerVirtualMachineInstanceResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestrg-scvmmvmi-%d" + location = "%s" +} + +resource "azurerm_arc_machine" "test" { + name = "acctest-arcmachine-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + kind = "SCVMM" +} + +resource "azurerm_system_center_virtual_machine_manager_server" "test" { + name = "acctest-scvmmms-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = "%s" + fqdn = "%s" + username = "%s" + password = "%s" +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, os.Getenv("ARM_TEST_CUSTOM_LOCATION_ID"), os.Getenv("ARM_TEST_FQDN"), os.Getenv("ARM_TEST_USERNAME"), os.Getenv("ARM_TEST_PASSWORD")) +} diff --git a/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_computer_name.go b/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_computer_name.go new file mode 100644 index 000000000000..d823e3b9a6ec --- /dev/null +++ b/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_computer_name.go @@ -0,0 +1,23 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validate + +import ( + "fmt" + "regexp" +) + +func SystemCenterVirtualMachineManagerVirtualMachineInstanceComputerName(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return + } + + if !regexp.MustCompile("^[a-zA-Z0-9]{1,}$").MatchString(v) { + errors = append(errors, fmt.Errorf("%q must only contain alphanumeric characters", k)) + } + + return warnings, errors +} diff --git a/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_computer_name_test.go b/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_computer_name_test.go new file mode 100644 index 000000000000..e1ca07bfeab7 --- /dev/null +++ b/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_computer_name_test.go @@ -0,0 +1,40 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validate + +import ( + "testing" +) + +func TestSystemCenterVirtualMachineManagerVirtualMachineInstanceComputerName(t *testing.T) { + testCases := []struct { + Input string + Expected bool + }{ + { + Input: "", + Expected: false, + }, + { + Input: "test-test", + Expected: false, + }, + { + Input: "s", + Expected: true, + }, + { + Input: "test123", + Expected: true, + }, + } + + for _, v := range testCases { + _, errors := SystemCenterVirtualMachineManagerVirtualMachineInstanceComputerName(v.Input, "test") + result := len(errors) == 0 + if result != v.Expected { + t.Fatalf("Expected the result to be %t but got %t (and %d errors)", v.Expected, result, len(errors)) + } + } +} diff --git a/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_mac_address.go b/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_mac_address.go new file mode 100644 index 000000000000..f4112160924f --- /dev/null +++ b/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_mac_address.go @@ -0,0 +1,23 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validate + +import ( + "fmt" + "regexp" +) + +func SystemCenterVirtualMachineManagerVirtualMachineInstanceMacAddress(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return + } + + if !regexp.MustCompile("^[a-fA-F0-9]{2}(:[a-fA-F0-9]{2}){5}$").MatchString(v) { + errors = append(errors, fmt.Errorf("%q must be in format `00:00:00:00:00:00`", k)) + } + + return warnings, errors +} diff --git a/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_mac_address_test.go b/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_mac_address_test.go new file mode 100644 index 000000000000..9db5bd2e4760 --- /dev/null +++ b/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_mac_address_test.go @@ -0,0 +1,32 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validate + +import ( + "testing" +) + +func TestSystemCenterVirtualMachineManagerVirtualMachineInstanceMacAddress(t *testing.T) { + testCases := []struct { + Input string + Expected bool + }{ + { + Input: "", + Expected: false, + }, + { + Input: "00:00:00:00:00:00", + Expected: true, + }, + } + + for _, v := range testCases { + _, errors := SystemCenterVirtualMachineManagerVirtualMachineInstanceMacAddress(v.Input, "test") + result := len(errors) == 0 + if result != v.Expected { + t.Fatalf("Expected the result to be %t but got %t (and %d errors)", v.Expected, result, len(errors)) + } + } +} diff --git a/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_storage_disk_name.go b/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_storage_disk_name.go new file mode 100644 index 000000000000..9f6340b15714 --- /dev/null +++ b/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_storage_disk_name.go @@ -0,0 +1,23 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validate + +import ( + "fmt" + "regexp" +) + +func SystemCenterVirtualMachineManagerVirtualMachineInstanceStorageDiskName(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return + } + + if !regexp.MustCompile("^[a-zA-Z]([-_a-zA-Z0-9]{0,78}[a-zA-Z0-9])?$").MatchString(v) { + errors = append(errors, fmt.Errorf("%q must start and end with a letter, may contain alphanumeric characters, dashes or underscores and must be between 1 and 80 characters long", k)) + } + + return warnings, errors +} diff --git a/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_storage_disk_name_test.go b/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_storage_disk_name_test.go new file mode 100644 index 000000000000..8e781bbfee23 --- /dev/null +++ b/internal/services/systemcentervirtualmachinemanager/validate/system_center_virtual_machine_manager_virtual_machine_instance_storage_disk_name_test.go @@ -0,0 +1,61 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validate + +import ( + "strings" + "testing" +) + +func TestSystemCenterVirtualMachineManagerVirtualMachineInstanceStorageDiskName(t *testing.T) { + testCases := []struct { + Input string + Expected bool + }{ + { + Input: "", + Expected: false, + }, + { + Input: "test-test_", + Expected: false, + }, + { + Input: "s", + Expected: true, + }, + { + Input: "test-_test", + Expected: true, + }, + { + Input: "test", + Expected: true, + }, + { + Input: "test_1", + Expected: true, + }, + { + Input: strings.Repeat("s", 79), + Expected: true, + }, + { + Input: strings.Repeat("s", 80), + Expected: true, + }, + { + Input: strings.Repeat("s", 81), + Expected: false, + }, + } + + for _, v := range testCases { + _, errors := SystemCenterVirtualMachineManagerVirtualMachineInstanceStorageDiskName(v.Input, "test") + result := len(errors) == 0 + if result != v.Expected { + t.Fatalf("Expected the result to be %t but got %t (and %d errors)", v.Expected, result, len(errors)) + } + } +} diff --git a/internal/services/systemcentervirtualmachinemanager/validate/virtual_machine_instance_id.go b/internal/services/systemcentervirtualmachinemanager/validate/virtual_machine_instance_id.go new file mode 100644 index 000000000000..bb585b7b04be --- /dev/null +++ b/internal/services/systemcentervirtualmachinemanager/validate/virtual_machine_instance_id.go @@ -0,0 +1,21 @@ +package validate + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/systemcentervirtualmachinemanager/parse" +) + +func SystemCenterVirtualMachineManagerVirtualMachineInstanceID(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.SystemCenterVirtualMachineManagerVirtualMachineInstanceID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/systemcentervirtualmachinemanager/validate/virtual_machine_instance_id_test.go b/internal/services/systemcentervirtualmachinemanager/validate/virtual_machine_instance_id_test.go new file mode 100644 index 000000000000..783edd0b8b41 --- /dev/null +++ b/internal/services/systemcentervirtualmachinemanager/validate/virtual_machine_instance_id_test.go @@ -0,0 +1,40 @@ +package validate + +import "testing" + +func TestSystemCenterVirtualMachineManagerVirtualMachineInstanceID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + { + Input: "", + Valid: false, + }, + { + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.HybridCompute/machines/machine1", + Valid: false, + }, + { + Input: "/providers/Microsoft.ScVmm/virtualMachineInstances/default", + Valid: false, + }, + { + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.ScVmm/virtualMachineInstances/default", + Valid: true, + }, + { + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.HYBRIDCOMPUTE/MACHINES/MACHINE1/PROVIDERS/MICROSOFT.SCVMM/VIRTUALMACHINEINSTANCES/DEFAULT", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := SystemCenterVirtualMachineManagerVirtualMachineInstanceID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/website/docs/r/system_center_virtual_machine_manager_virtual_machine_instance.html.markdown b/website/docs/r/system_center_virtual_machine_manager_virtual_machine_instance.html.markdown new file mode 100644 index 000000000000..64983ef456f5 --- /dev/null +++ b/website/docs/r/system_center_virtual_machine_manager_virtual_machine_instance.html.markdown @@ -0,0 +1,207 @@ +--- +subcategory: "System Center Virtual Machine Manager" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_system_center_virtual_machine_manager_virtual_machine_instance" +description: |- + Manages a System Center Virtual Machine Manager Virtual Machine Instance. +--- + +# azurerm_system_center_virtual_machine_manager_virtual_machine_instance + +Manages a System Center Virtual Machine Manager Virtual Machine Instance. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_arc_machine" "example" { + name = "example-arcmachine" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + kind = "SCVMM" +} + +resource "azurerm_system_center_virtual_machine_manager_server" "example" { + name = "example-scvmmms" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + custom_location_id = "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.ExtendedLocation/customLocations/customLocation1" + fqdn = "example.labtest" + username = "testUser" + password = "H@Sh1CoR3!" +} + +data "azurerm_system_center_virtual_machine_manager_inventory_items" "example" { + inventory_type = "Cloud" + system_center_virtual_machine_manager_server_id = azurerm_system_center_virtual_machine_manager_server.example.id +} + +resource "azurerm_system_center_virtual_machine_manager_cloud" "example" { + name = "example-scvmmc" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + custom_location_id = azurerm_system_center_virtual_machine_manager_server.example.custom_location_id + system_center_virtual_machine_manager_server_inventory_item_id = data.azurerm_system_center_virtual_machine_manager_inventory_items.example.inventory_items[0].id +} + +data "azurerm_system_center_virtual_machine_manager_inventory_items" "example2" { + inventory_type = "VirtualMachineTemplate" + system_center_virtual_machine_manager_server_id = azurerm_system_center_virtual_machine_manager_server.example.id +} + +resource "azurerm_system_center_virtual_machine_manager_virtual_machine_template" "example" { + name = "example-scvmmvmt" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + custom_location_id = azurerm_system_center_virtual_machine_manager_server.example.custom_location_id + system_center_virtual_machine_manager_server_inventory_item_id = data.azurerm_system_center_virtual_machine_manager_inventory_items.example2.inventory_items[0].id +} + +resource "azurerm_system_center_virtual_machine_manager_virtual_machine_instance" "example" { + scoped_resource_id = azurerm_arc_machine.example.id + custom_location_id = azurerm_system_center_virtual_machine_manager_server.example.custom_location_id + + infrastructure { + system_center_virtual_machine_manager_cloud_id = azurerm_system_center_virtual_machine_manager_cloud.example.id + system_center_virtual_machine_manager_template_id = azurerm_system_center_virtual_machine_manager_virtual_machine_template.example.id + system_center_virtual_machine_manager_virtual_machine_server_id = azurerm_system_center_virtual_machine_manager_server.example.id + } + + operating_system { + computer_name = "testComputer" + } + + hardware { + cpu_count = 1 + memory_in_mb = 1024 + } + + lifecycle { + // Service API always provisions a virtual disk with bus type IDE per Virtual Machine Template by default, so it has to be ignored + ignore_changes = [storage_disk] + } +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `scoped_resource_id` - (Required) The ID of the Hybrid Compute Machine where this System Center Virtual Machine Manager Virtual Machine Instance is stored. Changing this forces a new resource to be created. + +* `custom_location_id` - (Required) The ID of the Custom Location for the System Center Virtual Machine Manager Virtual Machine Instance. Changing this forces a new resource to be created. + +* `infrastructure` - (Required) An `infrastructure` block as defined below. + +* `hardware` - (Optional) A `hardware` block as defined below. Changing this forces a new resource to be created. + +~> **NOTE:** This resource will be restarted while updating `hardware`. + +* `network_interface` - (Optional) A `network_interface` block as defined below. + +~> **NOTE:** This resource will be restarted while updating `network_interface`. + +* `operating_system` - (Optional) An `operating_system` block as defined below. Changing this forces a new resource to be created. + +* `storage_disk` - (Optional) A `storage_disk` block as defined below. + +~> **NOTE:** This resource will be restarted while updating `storage_disk`. + +* `system_center_virtual_machine_manager_availability_set_ids` - (Optional) A list of IDs of System Center Virtual Machine Manager Availability Set. + +--- + +An `infrastructure` block supports the following: + +* `checkpoint_type` - (Optional) The type of checkpoint supported for the Virtual Machine. Possible values are `Disabled`, `Production`, `ProductionOnly` and `Standard`. + +* `system_center_virtual_machine_manager_cloud_id` - (Optional) The ID of the System Center Virtual Machine Manager Cloud resource to use for deploying the Virtual Machine. Changing this forces a new resource to be created. + +* `system_center_virtual_machine_manager_inventory_item_id` - (Optional) The ID of the System Center Virtual Machine Manager Inventory Item for System Center Virtual Machine Manager Virtual Machine Instance. Changing this forces a new resource to be created. + +* `system_center_virtual_machine_manager_template_id` - (Optional) The ID of the System Center Virtual Machine Manager Virtual Machine Template to use for deploying the Virtual Machine. Changing this forces a new resource to be created. + +* `system_center_virtual_machine_manager_virtual_machine_server_id` - (Optional) The ID of the System Center Virtual Machine Manager Virtual Machine. Changing this forces a new resource to be created. + +--- + +A `hardware` block supports the following: + +* `cpu_count` - (Optional) The number of vCPUs for the Virtual Machine. Possible values are between `1` and `64`. + +* `dynamic_memory_max_in_mb` - (Optional) The max dynamic memory for the Virtual Machine. Possible values are between `32` and `1048576`. + +* `dynamic_memory_min_in_mb` - (Optional) The min dynamic memory for the Virtual Machine. Possible values are between `32` and `1048576`. + +* `limit_cpu_for_migration_enabled` - (Optional) Whether processor compatibility mode for live migration of Virtual Machines is enabled. + +* `memory_in_mb` - (Optional) The size of a Virtual Machine's memory. Possible values are between `32` and `1048576`. + +--- + +A `network_interface` block supports the following: + +* `name` - (Required) The name of the Virtual Network in System Center Virtual Machine Manager Server that the Network Interface is connected to. + +* `virtual_network_id` - (Optional) The ID of the System Center Virtual Machine Manager Virtual Network to connect the Network Interface. + +* `ipv4_address_type` - (Optional) The IPv4 address type. Possible values are `Dynamic` and `Static`. + +* `ipv6_address_type` - (Optional) The IPv6 address type. Possible values are `Dynamic` and `Static`. + +* `mac_address_type` - (Optional) The MAC address type. Possible values are `Dynamic` and `Static`. + +--- + +An `operating_system` block supports the following: + +* `computer_name` - (Required) The computer name of the Virtual Machine. Changing this forces a new resource to be created. + +* `admin_password` - (Optional) The admin password of the Virtual Machine. Changing this forces a new resource to be created. + +--- + +A `storage_disk` block supports the following: + +* `bus` - (Optional) The disk bus. Possible values are between `0` and `3`. + +* `bus_type` - (Optional) The disk bus type. Possible values are `IDE` and `SCSI`. + +* `disk_size_gb` - (Optional) The disk total size. + +* `lun` - (Optional) The disk lun. Possible values are between `0` and `63`. + +* `name` - (Optional) The name of the disk. + +* `storage_qos_policy_name` - (Optional) The name of the Storage QoS policy. + +* `template_disk_id` - (Optional) The disk ID in the System Center Virtual Machine Manager Virtual Machine Template. Changing this forces a new resource to be created. + +* `vhd_type` - (Optional) The disk vhd type. Possible values are `Dynamic` and `Fixed`. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the System Center Virtual Machine Manager Virtual Machine Instance. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 60 minutes) Used when creating this System Center Virtual Machine Manager Virtual Machine Instance. +* `read` - (Defaults to 5 minutes) Used when retrieving this System Center Virtual Machine Manager Virtual Machine Instance. +* `update` - (Defaults to 60 minutes) Used when updating this System Center Virtual Machine Manager Virtual Machine Instance. +* `delete` - (Defaults to 60 minutes) Used when deleting this System Center Virtual Machine Manager Virtual Machine Instance. + +## Import + +System Center Virtual Machine Manager Virtual Machine Instances can be imported into Terraform using the `resource id`, e.g. + +```shell +terraform import azurerm_system_center_virtual_machine_manager_virtual_machine_instance.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.HybridCompute/machines/machine1/providers/Microsoft.ScVmm/virtualMachineInstances/default +```