From 8538e65c24b58f17111e5e2d2c857f7f386a2cac Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Thu, 18 Apr 2024 10:31:07 +0000 Subject: [PATCH 01/16] new resource stack_hci_deployment_setting --- .github/labeler-issue-triage.yml | 2 +- .../services/azurestackhci/registration.go | 4 +- .../stack_hci_deployment_setting_resource.go | 1328 +++++++++++++++++ ...ck_hci_deployment_setting_resource_test.go | 724 +++++++++ .../services/azurestackhci/testdata/ad.ps1 | 81 + .../azurestackhci/testdata/connect.ps1 | 130 ++ ...stack_hci_deployment_setting.html.markdown | 585 ++++++++ 7 files changed, 2852 insertions(+), 2 deletions(-) create mode 100644 internal/services/azurestackhci/stack_hci_deployment_setting_resource.go create mode 100644 internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go create mode 100644 internal/services/azurestackhci/testdata/ad.ps1 create mode 100644 internal/services/azurestackhci/testdata/connect.ps1 create mode 100644 website/docs/r/stack_hci_deployment_setting.html.markdown diff --git a/.github/labeler-issue-triage.yml b/.github/labeler-issue-triage.yml index 114f21bfa4ae..fa9487931f5f 100644 --- a/.github/labeler-issue-triage.yml +++ b/.github/labeler-issue-triage.yml @@ -46,7 +46,7 @@ service/automation: - '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_automation_((.|\n)*)###' service/azure-stack-hci: - - '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_stack_hci_cluster((.|\n)*)###' + - '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_stack_hci_((.|\n)*)###' service/batch: - '### (|New or )Affected Resource\(s\)\/Data Source\(s\)((.|\n)*)azurerm_batch_((.|\n)*)###' diff --git a/internal/services/azurestackhci/registration.go b/internal/services/azurestackhci/registration.go index 35ed508bbac2..20603f5b3a1f 100644 --- a/internal/services/azurestackhci/registration.go +++ b/internal/services/azurestackhci/registration.go @@ -48,5 +48,7 @@ func (r Registration) DataSources() []sdk.DataSource { } func (r Registration) Resources() []sdk.Resource { - return []sdk.Resource{} + return []sdk.Resource{ + StackHCIDeploymentSettingResource{}, + } } diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go new file mode 100644 index 000000000000..f984d9948369 --- /dev/null +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go @@ -0,0 +1,1328 @@ +package azurestackhci + +import ( + "context" + "fmt" + "regexp" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/deploymentsettings" + "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/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" +) + +var _ sdk.Resource = StackHCIDeploymentSettingResource{} + +type StackHCIDeploymentSettingResource struct{} + +func (StackHCIDeploymentSettingResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return deploymentsettings.ValidateDeploymentSettingID +} + +func (StackHCIDeploymentSettingResource) ResourceType() string { + return "azurerm_stack_hci_deployment_setting" +} + +type StackHCIDeploymentSettingModel struct { + StackHCIClusterId string `tfschema:"stack_hci_cluster_id"` + ArcResourceIds []string `tfschema:"arc_resource_ids"` + Version string `tfschema:"version"` + ScaleUnit []ScaleUnitModel `tfschema:"scale_unit"` +} + +type ScaleUnitModel struct { + AdouPath string `tfschema:"adou_path"` + Cluster []ClusterModel `tfschema:"cluster"` + DomainFqdn string `tfschema:"domain_fqdn"` + HostNetwork []HostNetworkModel `tfschema:"host_network"` + InfrastructureNetwork []InfrastructureNetworkModel `tfschema:"infrastructure_network"` + NamingPrefix string `tfschema:"naming_prefix"` + Observability []ObservabilityModel `tfschema:"observability"` + OptionalService []OptionalServiceModel `tfschema:"optional_service"` + PhysicalNode []PhysicalNodeModel `tfschema:"physical_node"` + SecretsLocation string `tfschema:"secrets_location"` + SecuritySetting []SecuritySettingModel `tfschema:"security_setting"` + Storage []StorageModel `tfschema:"storage"` +} + +type ClusterModel struct { + AzureServiceEndpoint string `tfschema:"azure_service_endpoint"` + CloudAccountName string `tfschema:"cloud_account_name"` + Name string `tfschema:"name"` + WitnessType string `tfschema:"witness_type"` + WitnessPath string `tfschema:"witness_path"` +} + +type HostNetworkModel struct { + Intent []HostNetworkIntentModel `tfschema:"intent"` + StorageAutoIpEnabled bool `tfschema:"storage_auto_ip_enabled"` + StorageConnectivitySwitchless bool `tfschema:"storage_connectivity_switchless"` + StorageNetwork []HostNetworkStorageNetworkModel `tfschema:"storage_network"` +} + +type HostNetworkIntentModel struct { + Adapter []string `tfschema:"adapter"` + AdapterPropertyOverride []HostNetworkIntentAdapterPropertyOverrideModel `tfschema:"adapter_property_override"` + Name string `tfschema:"name"` + OverrideAdapterPropertyEnabled bool `tfschema:"override_adapter_property_enabled"` + OverrideQosPolicyEnabled bool `tfschema:"override_qos_policy_enabled"` + OverrideVirtualSwitchConfigurationEnabled bool `tfschema:"override_virtual_switch_configuration_enabled"` + QosPolicyOverride []HostNetworkIntentQosPolicyOverrideModel `tfschema:"qos_policy_override"` + TrafficType []string `tfschema:"traffic_type"` + VirtualSwitchConfigurationOverride []HostNetworkIntentVirtualSwitchConfigurationOverrideModel `tfschema:"virtual_switch_configuration_override"` +} + +type HostNetworkIntentAdapterPropertyOverrideModel struct { + JumboPacket string `tfschema:"jumbo_packet"` + NetworkDirect string `tfschema:"network_direct"` + NetworkDirectTechnology string `tfschema:"network_direct_technology"` +} + +type HostNetworkIntentQosPolicyOverrideModel struct { + BandWidthPercentageSMB string `tfschema:"bandwidth_percentage_smb"` + PriorityValue8021ActionCluster string `tfschema:"priority_value8021_action_cluster"` + PriorityValue8021ActionSMB string `tfschema:"priority_value8021_action_smb"` +} + +type HostNetworkIntentVirtualSwitchConfigurationOverrideModel struct { + EnableIov string `tfschema:"enable_iov"` + LoadBalancingAlgorithm string `tfschema:"load_balancing_algorithm"` +} + +type HostNetworkStorageNetworkModel struct { + Name string `tfschema:"name"` + NetworkAdapterName string `tfschema:"network_adapter_name"` + VlanId string `tfschema:"vlan_id"` +} + +type InfrastructureNetworkModel struct { + DhcpEnabled bool `tfschema:"dhcp_enabled"` + SubnetMask string `tfschema:"subnet_mask"` + Gateway string `tfschema:"gateway"` + IpPool []IpPoolModel `tfschema:"ip_pool"` + DnsServer []string `tfschema:"dns_server"` +} + +type IpPoolModel struct { + StartingAddress string `tfschema:"starting_address"` + EndingAddress string `tfschema:"ending_address"` +} + +type ObservabilityModel struct { + StreamingDataClientEnabled bool `tfschema:"streaming_data_client_enabled"` + EuLocationEnabled bool `tfschema:"eu_location_enabled"` + EpisodicDataUploadEnabled bool `tfschema:"episodic_data_upload_enabled"` +} + +type OptionalServiceModel struct { + CustomLocation string `tfschema:"custom_location"` +} + +type PhysicalNodeModel struct { + Name string `tfschema:"name"` + Ipv4Address string `tfschema:"ipv4_address"` +} + +type SecuritySettingModel struct { + BitlockerBootVolumeEnabled bool `tfschema:"bitlocker_boot_volume_enabled"` + BitlockerDataVolumeEnabled bool `tfschema:"bitlocker_data_volume_enabled"` + CredentialGuardEnabled bool `tfschema:"credential_guard_enabled"` + DriftControlEnabled bool `tfschema:"drift_control_enabled"` + DrtmProtectionEnabled bool `tfschema:"drtm_protection_enabled"` + HvciProtectionEnabled bool `tfschema:"hvci_protection_enabled"` + SideChannelMitigationEnabled bool `tfschema:"side_channel_mitigation_enabled"` + SmbSigningEnabled bool `tfschema:"smb_signing_enabled"` + SmbClusterEncryptionEnabled bool `tfschema:"smb_cluster_encryption_enabled"` + WdacEnabled bool `tfschema:"wdac_enabled"` +} + +type StorageModel struct { + ConfigurationMode string `tfschema:"configuration_mode"` +} + +func (StackHCIDeploymentSettingResource) ModelObject() interface{} { + return &StackHCIDeploymentSettingModel{} +} + +func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "stack_hci_cluster_id": commonschema.ResourceIDReferenceRequiredForceNew(&deploymentsettings.ClusterId{}), + + "arc_resource_ids": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: machines.ValidateMachineID, + }, + }, + + "version": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$`), + "must be in format `10.0.0.1`", + ), + }, + + "scale_unit": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "adou_path": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "cluster": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile("^[a-zA-Z0-9-]{3,15}$"), + "must be 3-15 characters long and contain only letters, numbers and hyphens", + ), + }, + + "azure_service_endpoint": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "cloud_account_name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "witness_type": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + "Cloud", + "FileShare", + }, false), + }, + + "witness_path": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "domain_fqdn": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "host_network": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "intent": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "adapter": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "traffic_type": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + "Compute", + "Storage", + "Management", + }, false), + }, + }, + + "adapter_property_override": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "jumbo_packet": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "network_direct": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "network_direct_technology": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "override_adapter_property_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + }, + + "override_qos_policy_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + }, + + "override_virtual_switch_configuration_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + }, + + "qos_policy_override": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "bandwidth_percentage_smb": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "priority_value8021_action_cluster": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "priority_value8021_action_smb": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "virtual_switch_configuration_override": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "enable_iov": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "load_balancing_algorithm": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + }, + }, + }, + + "storage_network": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "network_adapter_name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "vlan_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "storage_auto_ip_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + Default: true, + }, + + "storage_connectivity_switchless": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, + }, + }, + }, + + "infrastructure_network": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "dns_server": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.IsIPv4Address, + }, + }, + + "gateway": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.IsIPv4Address, + }, + + "ip_pool": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "starting_address": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.IsIPv4Address, + }, + + "ending_address": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.IsIPv4Address, + }, + }, + }, + }, + + "subnet_mask": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.IsIPv4Address, + }, + + "dhcp_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + }, + }, + }, + }, + + "naming_prefix": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile("^[a-zA-Z0-9-]{1,8}$"), + "must be 1-8 characters long and contain only letters, numbers and hyphens", + ), + }, + + "optional_service": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "custom_location": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + + "physical_node": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "ipv4_address": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.IsIPv4Address, + }, + }, + }, + }, + + "secrets_location": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.IsURLWithHTTPS, + }, + + "storage": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "configuration_mode": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + "Express", + "InfraOnly", + "KeepStorage", + }, false), + }, + }, + }, + }, + + "observability": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "streaming_data_client_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + ForceNew: true, + }, + + "eu_location_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + ForceNew: true, + }, + + "episodic_data_upload_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + ForceNew: true, + }, + }, + }, + }, + + "security_setting": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "bitlocker_boot_volume_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + ForceNew: true, + }, + + "bitlocker_data_volume_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + ForceNew: true, + }, + + "credential_guard_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + ForceNew: true, + }, + + "drift_control_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + ForceNew: true, + }, + + "drtm_protection_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + ForceNew: true, + }, + + "hvci_protection_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + ForceNew: true, + }, + + "side_channel_mitigation_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + ForceNew: true, + }, + + "smb_signing_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + ForceNew: true, + }, + + "smb_cluster_encryption_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + ForceNew: true, + }, + + "wdac_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + ForceNew: true, + }, + }, + }, + }, + }, + }, + }, + } +} + +func (StackHCIDeploymentSettingResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (r StackHCIDeploymentSettingResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 6 * time.Hour, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.AzureStackHCI.DeploymentSettings + + var config StackHCIDeploymentSettingModel + if err := metadata.Decode(&config); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + stackHCIClusterId, err := deploymentsettings.ParseClusterID(config.StackHCIClusterId) + if err != nil { + return err + } + id := deploymentsettings.NewDeploymentSettingID(stackHCIClusterId.SubscriptionId, stackHCIClusterId.ResourceGroupName, stackHCIClusterId.ClusterName, "default") + + existing, err := client.Get(ctx, id) + 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 := deploymentsettings.DeploymentSetting{ + Properties: &deploymentsettings.DeploymentSettingsProperties{ + ArcNodeResourceIds: config.ArcResourceIds, + DeploymentMode: deploymentsettings.DeploymentModeValidate, + DeploymentConfiguration: deploymentsettings.DeploymentConfiguration{ + Version: pointer.To(config.Version), + ScaleUnits: ExpandDeploymentSettingScaleUnits(config.ScaleUnit), + }, + }, + } + + // the resource exists even validation error + metadata.SetID(id) + + // do validation + if err := client.CreateOrUpdateThenPoll(ctx, id, payload); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + // do deployment + payload.Properties.DeploymentMode = deploymentsettings.DeploymentModeDeploy + if err := client.CreateOrUpdateThenPoll(ctx, id, payload); err != nil { + return fmt.Errorf("deploying %s: %+v", id, err) + } + + return nil + }, + } +} + +func (StackHCIDeploymentSettingResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.AzureStackHCI.DeploymentSettings + + id, err := deploymentsettings.ParseDeploymentSettingID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(id) + } + + return fmt.Errorf("retrieving %s: %+v", id, err) + } + + schema := StackHCIDeploymentSettingModel{ + StackHCIClusterId: deploymentsettings.NewClusterID(id.SubscriptionId, id.ResourceGroupName, id.ClusterName).ID(), + } + + if model := resp.Model; model != nil { + if props := model.Properties; props != nil { + schema.ArcResourceIds = props.ArcNodeResourceIds + schema.Version = pointer.From(props.DeploymentConfiguration.Version) + schema.ScaleUnit = FlattenDeploymentSettingScaleUnits(props.DeploymentConfiguration.ScaleUnits) + } + } + + return metadata.Encode(&schema) + }, + } +} + +func (StackHCIDeploymentSettingResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 1 * time.Hour, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.AzureStackHCI.DeploymentSettings + + id, err := deploymentsettings.ParseDeploymentSettingID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + if err := client.DeleteThenPoll(ctx, *id); err != nil { + return fmt.Errorf("deleting %s: %+v", id, err) + } + + return nil + }, + } +} + +func ExpandDeploymentSettingScaleUnits(input []ScaleUnitModel) []deploymentsettings.ScaleUnits { + if len(input) == 0 { + return nil + } + + results := make([]deploymentsettings.ScaleUnits, 0, len(input)) + for _, item := range input { + results = append(results, deploymentsettings.ScaleUnits{ + DeploymentData: deploymentsettings.DeploymentData{ + AdouPath: pointer.To(item.AdouPath), + Cluster: ExpandDeploymentSettingCluster(item.Cluster), + DomainFqdn: pointer.To(item.DomainFqdn), + HostNetwork: ExpandDeploymentSettingHostNetwork(item.HostNetwork), + InfrastructureNetwork: ExpandDeploymentSettingInfrastructureNetwork(item.InfrastructureNetwork), + NamingPrefix: pointer.To(item.NamingPrefix), + Observability: ExpandDeploymentSettingObservability(item.Observability), + OptionalServices: ExpandDeploymentSettingOptionalService(item.OptionalService), + PhysicalNodes: ExpandDeploymentSettingPhysicalNode(item.PhysicalNode), + SecretsLocation: pointer.To(item.SecretsLocation), + SecuritySettings: ExpandDeploymentSettingSecuritySetting(item.SecuritySetting), + Storage: ExpandDeploymentSettingStorage(item.Storage), + }, + }) + } + + return results +} + +func FlattenDeploymentSettingScaleUnits(input []deploymentsettings.ScaleUnits) []ScaleUnitModel { + if len(input) == 0 { + return make([]ScaleUnitModel, 0) + } + + results := make([]ScaleUnitModel, 0, len(input)) + for _, item := range input { + results = append(results, ScaleUnitModel{ + AdouPath: pointer.From(item.DeploymentData.AdouPath), + Cluster: FlattenDeploymentSettingCluster(item.DeploymentData.Cluster), + DomainFqdn: pointer.From(item.DeploymentData.DomainFqdn), + HostNetwork: FlattenDeploymentSettingHostNetwork(item.DeploymentData.HostNetwork), + InfrastructureNetwork: FlattenDeploymentSettingInfrastructureNetwork(item.DeploymentData.InfrastructureNetwork), + NamingPrefix: pointer.From(item.DeploymentData.NamingPrefix), + Observability: FlattenDeploymentSettingObservability(item.DeploymentData.Observability), + OptionalService: FlattenDeploymentSettingOptionalService(item.DeploymentData.OptionalServices), + PhysicalNode: FlattenDeploymentSettingPhysicalNode(item.DeploymentData.PhysicalNodes), + SecretsLocation: pointer.From(item.DeploymentData.SecretsLocation), + SecuritySetting: FlattenDeploymentSettingSecuritySetting(item.DeploymentData.SecuritySettings), + Storage: FlattenDeploymentSettingStorage(item.DeploymentData.Storage), + }) + } + + return results +} + +func ExpandDeploymentSettingCluster(input []ClusterModel) *deploymentsettings.DeploymentCluster { + if len(input) == 0 { + return nil + } + + v := input[0] + + return &deploymentsettings.DeploymentCluster{ + AzureServiceEndpoint: pointer.To(v.AzureServiceEndpoint), + CloudAccountName: pointer.To(v.CloudAccountName), + Name: pointer.To(v.Name), + WitnessType: pointer.To(v.WitnessType), + WitnessPath: pointer.To(v.WitnessPath), + } +} + +func FlattenDeploymentSettingCluster(input *deploymentsettings.DeploymentCluster) []ClusterModel { + if input == nil { + return make([]ClusterModel, 0) + } + + return []ClusterModel{{ + AzureServiceEndpoint: pointer.From(input.AzureServiceEndpoint), + CloudAccountName: pointer.From(input.CloudAccountName), + Name: pointer.From(input.Name), + WitnessType: pointer.From(input.WitnessType), + WitnessPath: pointer.From(input.WitnessPath), + }} +} + +func ExpandDeploymentSettingHostNetwork(input []HostNetworkModel) *deploymentsettings.HostNetwork { + if len(input) == 0 { + return nil + } + + v := input[0] + + return &deploymentsettings.HostNetwork{ + Intents: ExpandDeploymentSettingHostNetworkIntent(v.Intent), + EnableStorageAutoIP: pointer.To(v.StorageAutoIpEnabled), + StorageConnectivitySwitchless: pointer.To(v.StorageConnectivitySwitchless), + StorageNetworks: ExpandDeploymentSettingHostNetworkStorageNetwork(v.StorageNetwork), + } +} + +func FlattenDeploymentSettingHostNetwork(input *deploymentsettings.HostNetwork) []HostNetworkModel { + if input == nil { + return make([]HostNetworkModel, 0) + } + + return []HostNetworkModel{{ + Intent: FlattenDeploymentSettingHostNetworkIntent(input.Intents), + StorageAutoIpEnabled: pointer.From(input.EnableStorageAutoIP), + StorageConnectivitySwitchless: pointer.From(input.StorageConnectivitySwitchless), + StorageNetwork: FlattenDeploymentSettingHostNetworkStorageNetwork(input.StorageNetworks), + }} +} + +func ExpandDeploymentSettingHostNetworkIntent(input []HostNetworkIntentModel) *[]deploymentsettings.Intents { + if len(input) == 0 { + return nil + } + + results := make([]deploymentsettings.Intents, 0, len(input)) + for _, item := range input { + results = append(results, deploymentsettings.Intents{ + Adapter: pointer.To(item.Adapter), + AdapterPropertyOverrides: ExpandHostNetworkIntentAdapterPropertyOverride(item.AdapterPropertyOverride), + Name: pointer.To(item.Name), + OverrideAdapterProperty: pointer.To(item.OverrideAdapterPropertyEnabled), + OverrideQosPolicy: pointer.To(item.OverrideQosPolicyEnabled), + OverrideVirtualSwitchConfiguration: pointer.To(item.OverrideVirtualSwitchConfigurationEnabled), + QosPolicyOverrides: ExpandHostNetworkIntentQosPolicyOverride(item.QosPolicyOverride), + TrafficType: pointer.To(item.TrafficType), + VirtualSwitchConfigurationOverrides: ExpandHostNetworkIntentVirtualSwitchConfigurationOverride(item.VirtualSwitchConfigurationOverride), + }) + } + + return &results +} + +func FlattenDeploymentSettingHostNetworkIntent(input *[]deploymentsettings.Intents) []HostNetworkIntentModel { + if input == nil { + return make([]HostNetworkIntentModel, 0) + } + + results := make([]HostNetworkIntentModel, 0, len(*input)) + for _, item := range *input { + results = append(results, HostNetworkIntentModel{ + Adapter: pointer.From(item.Adapter), + AdapterPropertyOverride: FlattenHostNetworkIntentAdapterPropertyOverride(item.AdapterPropertyOverrides), + Name: pointer.From(item.Name), + OverrideAdapterPropertyEnabled: pointer.From(item.OverrideAdapterProperty), + OverrideQosPolicyEnabled: pointer.From(item.OverrideQosPolicy), + OverrideVirtualSwitchConfigurationEnabled: pointer.From(item.OverrideVirtualSwitchConfiguration), + QosPolicyOverride: FlattenHostNetworkIntentQosPolicyOverride(item.QosPolicyOverrides), + TrafficType: pointer.From(item.TrafficType), + VirtualSwitchConfigurationOverride: FlattenHostNetworkIntentVirtualSwitchConfigurationOverride(item.VirtualSwitchConfigurationOverrides), + }) + } + + return results +} + +func ExpandHostNetworkIntentAdapterPropertyOverride(input []HostNetworkIntentAdapterPropertyOverrideModel) *deploymentsettings.AdapterPropertyOverrides { + if len(input) == 0 { + return &deploymentsettings.AdapterPropertyOverrides{ + JumboPacket: pointer.To(""), + NetworkDirect: pointer.To(""), + NetworkDirectTechnology: pointer.To(""), + } + } + + v := input[0] + + return &deploymentsettings.AdapterPropertyOverrides{ + JumboPacket: pointer.To(v.JumboPacket), + NetworkDirect: pointer.To(v.NetworkDirect), + NetworkDirectTechnology: pointer.To(v.NetworkDirectTechnology), + } +} + +func FlattenHostNetworkIntentAdapterPropertyOverride(input *deploymentsettings.AdapterPropertyOverrides) []HostNetworkIntentAdapterPropertyOverrideModel { + if input == nil { + return make([]HostNetworkIntentAdapterPropertyOverrideModel, 0) + } + + jumboPacket := pointer.From(input.JumboPacket) + networkDirect := pointer.From(input.NetworkDirect) + networkDirectTechnology := pointer.From(input.NetworkDirectTechnology) + + // server will return the block with empty string in all fields by default + if jumboPacket == "" && networkDirect == "" && networkDirectTechnology == "" { + return make([]HostNetworkIntentAdapterPropertyOverrideModel, 0) + } + + return []HostNetworkIntentAdapterPropertyOverrideModel{{ + JumboPacket: jumboPacket, + NetworkDirect: networkDirect, + NetworkDirectTechnology: networkDirectTechnology, + }} +} + +func ExpandHostNetworkIntentQosPolicyOverride(input []HostNetworkIntentQosPolicyOverrideModel) *deploymentsettings.QosPolicyOverrides { + if len(input) == 0 { + return &deploymentsettings.QosPolicyOverrides{ + BandwidthPercentageSMB: pointer.To(""), + PriorityValue8021ActionCluster: pointer.To(""), + PriorityValue8021ActionSMB: pointer.To(""), + } + } + + v := input[0] + + return &deploymentsettings.QosPolicyOverrides{ + BandwidthPercentageSMB: pointer.To(v.BandWidthPercentageSMB), + PriorityValue8021ActionCluster: pointer.To(v.PriorityValue8021ActionCluster), + PriorityValue8021ActionSMB: pointer.To(v.PriorityValue8021ActionSMB), + } +} + +func FlattenHostNetworkIntentQosPolicyOverride(input *deploymentsettings.QosPolicyOverrides) []HostNetworkIntentQosPolicyOverrideModel { + if input == nil { + return make([]HostNetworkIntentQosPolicyOverrideModel, 0) + } + + bandwidthPercentageSMB := pointer.From(input.BandwidthPercentageSMB) + priorityValue8021ActionCluster := pointer.From(input.PriorityValue8021ActionCluster) + priorityValue8021ActionSMB := pointer.From(input.PriorityValue8021ActionSMB) + + // server will return the block with empty string in all fields by default + if bandwidthPercentageSMB == "" && priorityValue8021ActionCluster == "" && priorityValue8021ActionSMB == "" { + return make([]HostNetworkIntentQosPolicyOverrideModel, 0) + } + + return []HostNetworkIntentQosPolicyOverrideModel{{ + BandWidthPercentageSMB: bandwidthPercentageSMB, + PriorityValue8021ActionCluster: priorityValue8021ActionCluster, + PriorityValue8021ActionSMB: priorityValue8021ActionSMB, + }} +} + +func ExpandHostNetworkIntentVirtualSwitchConfigurationOverride(input []HostNetworkIntentVirtualSwitchConfigurationOverrideModel) *deploymentsettings.VirtualSwitchConfigurationOverrides { + if len(input) == 0 { + return &deploymentsettings.VirtualSwitchConfigurationOverrides{ + EnableIov: pointer.To(""), + LoadBalancingAlgorithm: pointer.To(""), + } + } + + v := input[0] + + return &deploymentsettings.VirtualSwitchConfigurationOverrides{ + EnableIov: pointer.To(v.EnableIov), + LoadBalancingAlgorithm: pointer.To(v.LoadBalancingAlgorithm), + } +} + +func FlattenHostNetworkIntentVirtualSwitchConfigurationOverride(input *deploymentsettings.VirtualSwitchConfigurationOverrides) []HostNetworkIntentVirtualSwitchConfigurationOverrideModel { + if input == nil { + return make([]HostNetworkIntentVirtualSwitchConfigurationOverrideModel, 0) + } + + enableIov := pointer.From(input.EnableIov) + loadBalancingAlgorithm := pointer.From(input.LoadBalancingAlgorithm) + + // server will return the block with empty string in all fields by default + if enableIov == "" && loadBalancingAlgorithm == "" { + return make([]HostNetworkIntentVirtualSwitchConfigurationOverrideModel, 0) + } + + return []HostNetworkIntentVirtualSwitchConfigurationOverrideModel{{ + EnableIov: enableIov, + LoadBalancingAlgorithm: loadBalancingAlgorithm, + }} +} + +func ExpandDeploymentSettingHostNetworkStorageNetwork(input []HostNetworkStorageNetworkModel) *[]deploymentsettings.StorageNetworks { + if len(input) == 0 { + return nil + } + + results := make([]deploymentsettings.StorageNetworks, 0, len(input)) + for _, item := range input { + results = append(results, deploymentsettings.StorageNetworks{ + Name: pointer.To(item.Name), + NetworkAdapterName: pointer.To(item.NetworkAdapterName), + VlanId: pointer.To(item.VlanId), + }) + } + + return &results +} + +func FlattenDeploymentSettingHostNetworkStorageNetwork(input *[]deploymentsettings.StorageNetworks) []HostNetworkStorageNetworkModel { + if input == nil { + return make([]HostNetworkStorageNetworkModel, 0) + } + + results := make([]HostNetworkStorageNetworkModel, 0, len(*input)) + for _, item := range *input { + results = append(results, HostNetworkStorageNetworkModel{ + Name: pointer.From(item.Name), + NetworkAdapterName: pointer.From(item.NetworkAdapterName), + VlanId: pointer.From(item.VlanId), + }) + } + + return results +} + +func ExpandDeploymentSettingInfrastructureNetwork(input []InfrastructureNetworkModel) *[]deploymentsettings.InfrastructureNetwork { + if len(input) == 0 { + return nil + } + + results := make([]deploymentsettings.InfrastructureNetwork, 0, len(input)) + for _, item := range input { + results = append(results, deploymentsettings.InfrastructureNetwork{ + DnsServers: pointer.To(item.DnsServer), + Gateway: pointer.To(item.Gateway), + IPPools: ExpandDeploymentSettingInfrastructureNetworkIpPool(item.IpPool), + SubnetMask: pointer.To(item.SubnetMask), + UseDhcp: pointer.To(item.DhcpEnabled), + }) + } + + return &results +} + +func FlattenDeploymentSettingInfrastructureNetwork(input *[]deploymentsettings.InfrastructureNetwork) []InfrastructureNetworkModel { + if input == nil { + return make([]InfrastructureNetworkModel, 0) + } + + results := make([]InfrastructureNetworkModel, 0, len(*input)) + for _, item := range *input { + results = append(results, InfrastructureNetworkModel{ + DhcpEnabled: pointer.From(item.UseDhcp), + DnsServer: pointer.From(item.DnsServers), + Gateway: pointer.From(item.Gateway), + IpPool: FlattenDeploymentSettingInfrastructureNetworkIpPool(item.IPPools), + SubnetMask: pointer.From(item.SubnetMask), + }) + } + + return results +} + +func ExpandDeploymentSettingInfrastructureNetworkIpPool(input []IpPoolModel) *[]deploymentsettings.IPPools { + if len(input) == 0 { + return nil + } + + results := make([]deploymentsettings.IPPools, 0, len(input)) + for _, item := range input { + results = append(results, deploymentsettings.IPPools{ + EndingAddress: pointer.To(item.EndingAddress), + StartingAddress: pointer.To(item.StartingAddress), + }) + } + + return &results +} + +func FlattenDeploymentSettingInfrastructureNetworkIpPool(input *[]deploymentsettings.IPPools) []IpPoolModel { + if input == nil { + return make([]IpPoolModel, 0) + } + + results := make([]IpPoolModel, 0, len(*input)) + for _, item := range *input { + results = append(results, IpPoolModel{ + EndingAddress: pointer.From(item.EndingAddress), + StartingAddress: pointer.From(item.StartingAddress), + }) + } + + return results +} + +func ExpandDeploymentSettingObservability(input []ObservabilityModel) *deploymentsettings.Observability { + if len(input) == 0 { + return nil + } + + v := input[0] + + return &deploymentsettings.Observability{ + EpisodicDataUpload: pointer.To(v.EpisodicDataUploadEnabled), + EuLocation: pointer.To(v.EuLocationEnabled), + StreamingDataClient: pointer.To(v.StreamingDataClientEnabled), + } +} + +func FlattenDeploymentSettingObservability(input *deploymentsettings.Observability) []ObservabilityModel { + if input == nil { + return make([]ObservabilityModel, 0) + } + + return []ObservabilityModel{{ + EpisodicDataUploadEnabled: pointer.From(input.EpisodicDataUpload), + EuLocationEnabled: pointer.From(input.EuLocation), + StreamingDataClientEnabled: pointer.From(input.StreamingDataClient), + }} +} + +func ExpandDeploymentSettingOptionalService(input []OptionalServiceModel) *deploymentsettings.OptionalServices { + if len(input) == 0 { + return nil + } + + v := input[0] + + return &deploymentsettings.OptionalServices{ + CustomLocation: pointer.To(v.CustomLocation), + } +} + +func FlattenDeploymentSettingOptionalService(input *deploymentsettings.OptionalServices) []OptionalServiceModel { + if input == nil { + return make([]OptionalServiceModel, 0) + } + + return []OptionalServiceModel{{ + CustomLocation: pointer.From(input.CustomLocation), + }} +} + +func ExpandDeploymentSettingPhysicalNode(input []PhysicalNodeModel) *[]deploymentsettings.PhysicalNodes { + if len(input) == 0 { + return nil + } + + results := make([]deploymentsettings.PhysicalNodes, 0, len(input)) + for _, item := range input { + results = append(results, deploymentsettings.PhysicalNodes{ + IPv4Address: pointer.To(item.Ipv4Address), + Name: pointer.To(item.Name), + }) + } + + return &results +} + +func FlattenDeploymentSettingPhysicalNode(input *[]deploymentsettings.PhysicalNodes) []PhysicalNodeModel { + if input == nil { + return make([]PhysicalNodeModel, 0) + } + + results := make([]PhysicalNodeModel, 0, len(*input)) + for _, item := range *input { + results = append(results, PhysicalNodeModel{ + Ipv4Address: pointer.From(item.IPv4Address), + Name: pointer.From(item.Name), + }) + } + + return results +} + +func ExpandDeploymentSettingSecuritySetting(input []SecuritySettingModel) *deploymentsettings.DeploymentSecuritySettings { + if len(input) == 0 { + return nil + } + + v := input[0] + + return &deploymentsettings.DeploymentSecuritySettings{ + BitlockerBootVolume: pointer.To(v.BitlockerBootVolumeEnabled), + BitlockerDataVolumes: pointer.To(v.BitlockerDataVolumeEnabled), + CredentialGuardEnforced: pointer.To(v.CredentialGuardEnabled), + DriftControlEnforced: pointer.To(v.DriftControlEnabled), + DrtmProtection: pointer.To(v.DrtmProtectionEnabled), + HvciProtection: pointer.To(v.HvciProtectionEnabled), + SideChannelMitigationEnforced: pointer.To(v.SideChannelMitigationEnabled), + SmbClusterEncryption: pointer.To(v.SmbClusterEncryptionEnabled), + SmbSigningEnforced: pointer.To(v.SmbSigningEnabled), + WdacEnforced: pointer.To(v.WdacEnabled), + } +} + +func FlattenDeploymentSettingSecuritySetting(input *deploymentsettings.DeploymentSecuritySettings) []SecuritySettingModel { + if input == nil { + return make([]SecuritySettingModel, 0) + } + + return []SecuritySettingModel{{ + BitlockerBootVolumeEnabled: pointer.From(input.BitlockerBootVolume), + BitlockerDataVolumeEnabled: pointer.From(input.BitlockerDataVolumes), + CredentialGuardEnabled: pointer.From(input.CredentialGuardEnforced), + DriftControlEnabled: pointer.From(input.DriftControlEnforced), + DrtmProtectionEnabled: pointer.From(input.DrtmProtection), + HvciProtectionEnabled: pointer.From(input.HvciProtection), + SideChannelMitigationEnabled: pointer.From(input.SideChannelMitigationEnforced), + SmbClusterEncryptionEnabled: pointer.From(input.SmbClusterEncryption), + SmbSigningEnabled: pointer.From(input.SmbSigningEnforced), + WdacEnabled: pointer.From(input.WdacEnforced), + }} +} + +func ExpandDeploymentSettingStorage(input []StorageModel) *deploymentsettings.Storage { + if len(input) == 0 { + return nil + } + + v := input[0] + + return &deploymentsettings.Storage{ + ConfigurationMode: pointer.To(v.ConfigurationMode), + } +} + +func FlattenDeploymentSettingStorage(input *deploymentsettings.Storage) []StorageModel { + if input == nil { + return make([]StorageModel, 0) + } + + return []StorageModel{{ + ConfigurationMode: pointer.From(input.ConfigurationMode), + }} +} diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go new file mode 100644 index 000000000000..e8979cd98dcd --- /dev/null +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go @@ -0,0 +1,724 @@ +package azurestackhci_test + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/deploymentsettings" + "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/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type StackHCIDeploymentSettingResource struct{} + +// The test must run in Windows environment with Administrator PowerShell, which will simulate HCI hardware by an Azure Windows VM based on a given image, and deploy HCI on it. +// based on https://github.com/Azure/Edge-infrastructure-quickstart-template +const ( + imageIdEnv = "ARM_TEST_HCI_IMAGE_ID" + localAdminUserEnv = "ARM_TEST_HCI_LOCAL_ADMIN_USER" + localAdminUserPasswordEnv = "ARM_TEST_HCI_LOCAL_ADMIN_USER_PASSWORD" + domainAdminUserEnv = "ARM_TEST_HCI_DOMAIN_ADMIN_USER" + domainAdminUserPasswordEnv = "ARM_TEST_HCI_DOMAIN_ADMIN_USER_PASSWORD" + deploymentUserEnv = "ARM_TEST_HCI_DEPLOYMENT_USER" + deploymentUserPasswordEnv = "ARM_TEST_HCI_DEPLOYMENT_USER_PASSWORD" +) + +func TestAccStackHCIDeploymentSetting_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_stack_hci_deployment_setting", "test") + r := StackHCIDeploymentSettingResource{} + + if os.Getenv(imageIdEnv) == "" || os.Getenv(localAdminUserEnv) == "" || os.Getenv(domainAdminUserEnv) == "" || os.Getenv(domainAdminUserEnv) == "" || os.Getenv(domainAdminUserPasswordEnv) == "" || os.Getenv(deploymentUserEnv) == "" || os.Getenv(deploymentUserPasswordEnv) == "" { + t.Skipf("skip the test as one or more of below environment variables are not specified: %q, %q, %q, %q, %q, %q, %q", imageIdEnv, localAdminUserEnv, localAdminUserPasswordEnv, domainAdminUserEnv, domainAdminUserPasswordEnv, deploymentUserEnv, deploymentUserPasswordEnv) + } + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccStackHCIDeploymentSetting_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_stack_hci_deployment_setting", "test") + r := StackHCIDeploymentSettingResource{} + + if os.Getenv(imageIdEnv) == "" || os.Getenv(localAdminUserEnv) == "" || os.Getenv(domainAdminUserEnv) == "" || os.Getenv(domainAdminUserEnv) == "" || os.Getenv(domainAdminUserPasswordEnv) == "" || os.Getenv(deploymentUserEnv) == "" || os.Getenv(deploymentUserPasswordEnv) == "" { + t.Skipf("skip the test as one or more of below environment variables are not specified: %q, %q, %q, %q, %q, %q, %q", imageIdEnv, localAdminUserEnv, localAdminUserPasswordEnv, domainAdminUserEnv, domainAdminUserPasswordEnv, deploymentUserEnv, deploymentUserPasswordEnv) + } + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccStackHCIDeploymentSetting_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_stack_hci_deployment_setting", "test") + r := StackHCIDeploymentSettingResource{} + + if os.Getenv(imageIdEnv) == "" || os.Getenv(localAdminUserEnv) == "" || os.Getenv(domainAdminUserEnv) == "" || os.Getenv(domainAdminUserEnv) == "" || os.Getenv(domainAdminUserPasswordEnv) == "" || os.Getenv(deploymentUserEnv) == "" || os.Getenv(deploymentUserPasswordEnv) == "" { + t.Skipf("skip the test as one or more of below environment variables are not specified: %q, %q, %q, %q, %q, %q, %q", imageIdEnv, localAdminUserEnv, localAdminUserPasswordEnv, domainAdminUserEnv, domainAdminUserPasswordEnv, deploymentUserEnv, deploymentUserPasswordEnv) + } + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r StackHCIDeploymentSettingResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + clusterClient := client.AzureStackHCI.DeploymentSettings + id, err := deploymentsettings.ParseDeploymentSettingID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clusterClient.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return utils.Bool(false), nil + } + + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) + } + + return utils.Bool(resp.Model != nil), nil +} + +func (r StackHCIDeploymentSettingResource) basic(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +provider "azurerm" { + features {} +} + +resource "azurerm_stack_hci_deployment_setting" "test" { + stack_hci_cluster_id = azurerm_stack_hci_cluster.test.id + arc_resource_ids = [for server in data.azurerm_arc_machine.server : server.id] + version = "10.0.0.0" + + scale_unit { + adou_path = "OU=hci${var.random_string},DC=jumpstart,DC=local" + domain_fqdn = "jumpstart.local" + secrets_location = azurerm_key_vault.DeploymentKeyVault.vault_uri + naming_prefix = "hci${var.random_string}" + + cluster { + azure_service_endpoint = "core.windows.net" + cloud_account_name = azurerm_storage_account.witness.name + name = azurerm_stack_hci_cluster.test.name + witness_type = "Cloud" + witness_path = "Cloud" + } + + host_network { + intent { + name = "ManagementCompute" + adapter = [ + "FABRIC", + "FABRIC2", + ] + traffic_type = [ + "Management", + "Compute", + ] + } + + intent { + name = "Storage" + adapter = [ + "StorageA", + "StorageB", + ] + traffic_type = [ + "Storage", + ] + } + + storage_network { + name = "Storage1Network" + network_adapter_name = "StorageA" + vlan_id = "711" + } + + storage_network { + name = "Storage2Network" + network_adapter_name = "StorageB" + vlan_id = "712" + } + } + + infrastructure_network { + gateway = "192.168.1.1" + subnet_mask = "255.255.255.0" + dns_server = [ + "192.168.1.254" + ] + ip_pool { + ending_address = "192.168.1.65" + starting_address = "192.168.1.55" + } + } + + optional_service { + custom_location = "customlocation${var.random_string}" + } + + physical_node { + ipv4_address = "192.168.1.12" + name = "AzSHOST1" + } + + physical_node { + ipv4_address = "192.168.1.13" + name = "AzSHOST2" + } + + observability { + streaming_data_client_enabled = true + eu_location_enabled = false + episodic_data_upload_enabled = true + } + + security_setting { + bitlocker_boot_volume_enabled = true + bitlocker_data_volume_enabled = true + credential_guard_enabled = true + drift_control_enabled = true + drtm_protection_enabled = true + hvci_protection_enabled = true + side_channel_mitigation_enabled = true + smb_cluster_encryption_enabled = false + smb_signing_enabled = true + wdac_enabled = true + } + storage { + configuration_mode = "Express" + } + } +} +`, template) +} + +func (r StackHCIDeploymentSettingResource) requiresImport(data acceptance.TestData) string { + config := r.basic(data) + + return fmt.Sprintf(` +%s + +provider "azurerm" { + features {} +} + +resource "azurerm_stack_hci_deployment_setting" "import" { + stack_hci_cluster_id = azurerm_stack_hci_deployment_setting.test.stack_hci_cluster_id + version = azurerm_stack_hci_deployment_setting.test.version + arc_resource_ids = azurerm_stack_hci_deployment_setting.test.arc_resource_ids + scale_unit = azurerm_stack_hci_deployment_setting.test.scale_unit +} +`, config) +} + +func (r StackHCIDeploymentSettingResource) complete(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +provider "azurerm" { + features {} +} + +resource "azurerm_stack_hci_deployment_setting" "test" { + stack_hci_cluster_id = azurerm_stack_hci_cluster.test.id + arc_resource_ids = [for server in data.azurerm_arc_machine.server : server.id] + version = "10.0.0.0" + + scale_unit { + adou_path = "OU=hci${var.random_string},DC=jumpstart,DC=local" + domain_fqdn = "jumpstart.local" + secrets_location = azurerm_key_vault.DeploymentKeyVault.vault_uri + naming_prefix = "hci${var.random_string}" + + cluster { + azure_service_endpoint = "core.windows.net" + cloud_account_name = azurerm_storage_account.witness.name + name = azurerm_stack_hci_cluster.test.name + witness_type = "Cloud" + witness_path = "Cloud" + } + + host_network { + storage_auto_ip_enabled = true + storage_connectivity_switchless = false + intent { + name = "ManagementCompute" + override_adapter_property_enabled = false + override_qos_policy_enabled = false + override_virtual_switch_configuration_enabled = false + adapter = [ + "FABRIC", + "FABRIC2", + ] + traffic_type = [ + "Management", + "Compute", + ] + qos_policy_override { + priority_value8021_action_cluster = "7" + priority_value8021_action_smb = "3" + bandwidth_percentage_smb = "50" + } + adapter_property_override { + jumbo_packet = "9014" + network_direct = "Disabled" + network_direct_technology = "RoCEv2" + } + } + + intent { + name = "Storage" + override_adapter_property_enabled = false + override_qos_policy_enabled = false + override_virtual_switch_configuration_enabled = false + adapter = [ + "StorageA", + "StorageB", + ] + traffic_type = [ + "Storage", + ] + qos_policy_override { + priority_value8021_action_cluster = "7" + priority_value8021_action_smb = "3" + bandwidth_percentage_smb = "50" + } + adapter_property_override { + jumbo_packet = "9014" + network_direct = "Enabled" + network_direct_technology = "RoCEv2" + } + } + + storage_network { + name = "Storage1Network" + network_adapter_name = "StorageA" + vlan_id = "711" + } + + storage_network { + name = "Storage2Network" + network_adapter_name = "StorageB" + vlan_id = "712" + } + } + + infrastructure_network { + gateway = "192.168.1.1" + subnet_mask = "255.255.255.0" + dhcp_enabled = false + dns_server = [ + "192.168.1.254" + ] + ip_pool { + ending_address = "192.168.1.65" + starting_address = "192.168.1.55" + } + } + + optional_service { + custom_location = "customlocation${var.random_string}" + } + + physical_node { + ipv4_address = "192.168.1.12" + name = "AzSHOST1" + } + + physical_node { + ipv4_address = "192.168.1.13" + name = "AzSHOST2" + } + + observability { + streaming_data_client_enabled = true + eu_location_enabled = false + episodic_data_upload_enabled = true + } + + security_setting { + bitlocker_boot_volume_enabled = true + bitlocker_data_volume_enabled = true + credential_guard_enabled = true + drift_control_enabled = true + drtm_protection_enabled = true + hvci_protection_enabled = true + side_channel_mitigation_enabled = true + smb_cluster_encryption_enabled = false + smb_signing_enabled = true + wdac_enabled = true + } + + storage { + configuration_mode = "Express" + } + } +} +`, template) +} + +func (r StackHCIDeploymentSettingResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +variable "primary_location" { + default = %q +} + +variable "random_string" { + default = %q +} + +variable "image_id" { + description = "The ID of the image to use for the HCI virtual host machine" + sensitive = true + type = string + default = %q +} + +variable "local_admin_user" { + description = "The username of the local administrator account." + sensitive = true + type = string + default = %q +} + +variable "local_admin_password" { + description = "The password of the local administrator account." + sensitive = true + type = string + default = %q +} + +variable "domain_admin_user" { + description = "The username of the domain account." + sensitive = true + type = string + default = %q +} + +variable "domain_admin_password" { + description = "The password of the domain account." + sensitive = true + type = string + default = %q +} + +variable "deployment_user" { + sensitive = true + type = string + description = "The username for deployment user." + default = %q +} + +variable "deployment_user_password" { + sensitive = true + type = string + description = "The password for deployment user." + default = %q +} + +provider "azuread" {} + +data "azurerm_subscription" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctest-hci-vm-${var.random_string}" + location = var.primary_location +} + +resource "azurerm_public_ip" "test" { + name = "ip-${var.random_string}" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + allocation_method = "Static" + sku = "Standard" +} + +resource "azurerm_virtual_network" "test" { + name = "vnet-${var.random_string}" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + address_space = ["172.17.0.0/16"] +} + +resource "azurerm_subnet" "test" { + name = "default" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["172.17.0.0/24"] +} + +resource "azurerm_network_interface" "test" { + name = "ni-${var.random_string}" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + ip_configuration { + name = "ipconfig1" + private_ip_address_allocation = "Dynamic" + public_ip_address_id = azurerm_public_ip.test.id + subnet_id = azurerm_subnet.test.id + } +} + +resource "azurerm_virtual_machine" "test" { + name = "vm-${var.random_string}" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + network_interface_ids = [azurerm_network_interface.test.id] + vm_size = "Standard_E32s_v5" + delete_os_disk_on_termination = true + delete_data_disks_on_termination = true + storage_image_reference { + id = var.image_id + } + boot_diagnostics { + enabled = true + storage_uri = "" + } + + storage_os_disk { + create_option = "FromImage" + name = "vm-${var.random_string}_OsDisk" + } +} + +locals { + servers = [ + { + name = "AzSHOST1" + ipv4_address = "192.168.1.12" + port = 15985 + }, + { + name = "AzSHOST2" + ipv4_address = "192.168.1.13" + port = 25985 + } + ] + connection_roles = [ + "Azure Connected Machine Onboarding", + "Azure Connected Machine Resource Administrator", + "Azure Resource Bridge Deployment Role" + ] + machine_roles = [ + "Key Vault Secrets User", + "Azure Connected Machine Resource Manager", + "Azure Stack HCI Device Management Role", + "Reader" + ] +} + +resource "azurerm_resource_group" "test2" { + name = "acctest-hci-${var.random_string}" + location = var.primary_location +} + +resource "azuread_application" "test" { + display_name = "acctest-hci-onboard-${var.random_string}" +} + +resource "azuread_service_principal" "test" { + application_id = azuread_application.test.application_id +} + +resource "azuread_service_principal_password" "test" { + service_principal_id = azuread_service_principal.test.object_id +} + +resource "azurerm_role_assignment" "connect" { + count = length(local.connection_roles) + scope = azurerm_resource_group.test2.id + role_definition_name = local.connection_roles[count.index] + principal_id = azuread_service_principal.test.object_id +} + +// this is following https://learn.microsoft.com/en-us/azure-stack/hci/deploy/deployment-tool-active-directory +resource "terraform_data" "ad_creation_provisioner" { + depends_on = [azurerm_virtual_machine.test] + + provisioner "local-exec" { + command = "powershell.exe -ExecutionPolicy Bypass -NoProfile -File ./testdata/ad.ps1 -userName ${var.domain_admin_user} -password \"${var.domain_admin_password}\" -authType Credssp -ip ${azurerm_public_ip.test.ip_address} -port 6985 -adouPath OU=hci${var.random_string},DC=jumpstart,DC=local -domainFqdn jumpstart.local -ifdeleteadou false -deploymentUserName ${var.deployment_user} -deploymentUserPassword \"${var.deployment_user_password}\"" + interpreter = ["PowerShell", "-Command"] + } + + lifecycle { + replace_triggered_by = [azurerm_resource_group.test2.name] + } +} + +resource "terraform_data" "provisioner" { + count = length(local.servers) + + depends_on = [ + terraform_data.ad_creation_provisioner, + azurerm_role_assignment.connect, + ] + + provisioner "local-exec" { + command = "echo Connect ${local.servers[count.index].name} to Azure Arc..." + } + + provisioner "local-exec" { + command = "powershell.exe -ExecutionPolicy Bypass -NoProfile -File ./testdata/connect.ps1 -userName ${var.local_admin_user} -password \"${var.local_admin_password}\" -authType Credssp -ip ${azurerm_public_ip.test.ip_address} -port ${local.servers[count.index].port} -subscriptionId ${data.azurerm_client_config.current.subscription_id} -resourceGroupName ${azurerm_resource_group.test2.name} -region ${azurerm_resource_group.test2.location} -tenant ${data.azurerm_client_config.current.tenant_id} -servicePrincipalId ${azuread_service_principal.test.object_id} -servicePrincipalSecret ${azuread_service_principal_password.test.value} -expandC true" + interpreter = ["PowerShell", "-Command"] + } + + provisioner "local-exec" { + command = "echo connected ${local.servers[count.index].name}" + } + + lifecycle { + replace_triggered_by = [azurerm_resource_group.test2.name] + } +} + +data "azurerm_arc_machine" "server" { + count = length(local.servers) + name = local.servers[count.index].name + resource_group_name = azurerm_resource_group.test2.name + depends_on = [terraform_data.provisioner] +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_key_vault" "DeploymentKeyVault" { + name = "hci${var.random_string}-testkv" + location = azurerm_resource_group.test2.location + resource_group_name = azurerm_resource_group.test2.name + enabled_for_deployment = true + enabled_for_template_deployment = true + enabled_for_disk_encryption = true + tenant_id = data.azurerm_client_config.current.tenant_id + soft_delete_retention_days = 30 + enable_rbac_authorization = true + public_network_access_enabled = true + sku_name = "standard" +} + +resource "azurerm_role_assignment" "KeyVault" { + scope = azurerm_key_vault.DeploymentKeyVault.id + role_definition_name = "Key Vault Secrets Officer" + principal_id = data.azurerm_client_config.current.object_id +} + +resource "azurerm_key_vault_secret" "AzureStackLCMUserCredential" { + name = "AzureStackLCMUserCredential" + content_type = "Secret" + value = base64encode("${var.deployment_user}:${var.deployment_user_password}") + key_vault_id = azurerm_key_vault.DeploymentKeyVault.id + depends_on = [azurerm_role_assignment.KeyVault] +} + +resource "azurerm_key_vault_secret" "LocalAdminCredential" { + name = "LocalAdminCredential" + content_type = "Secret" + value = base64encode("${var.local_admin_user}:${var.local_admin_password}") + key_vault_id = azurerm_key_vault.DeploymentKeyVault.id + depends_on = [azurerm_role_assignment.KeyVault] +} + +resource "azurerm_key_vault_secret" "DefaultARBApplication" { + name = "DefaultARBApplication" + content_type = "Secret" + value = base64encode("${azuread_service_principal.test.object_id}:${azuread_service_principal_password.test.value}") + key_vault_id = azurerm_key_vault.DeploymentKeyVault.id + depends_on = [azurerm_role_assignment.KeyVault] +} + +resource "azurerm_key_vault_secret" "WitnessStorageKey" { + name = "WitnessStorageKey" + content_type = "Secret" + value = base64encode(azurerm_storage_account.witness.primary_access_key) + key_vault_id = azurerm_key_vault.DeploymentKeyVault.id + depends_on = [azurerm_role_assignment.KeyVault] +} + +resource "azurerm_storage_account" "witness" { + name = "hci${var.random_string}teststa" + location = azurerm_resource_group.test2.location + resource_group_name = azurerm_resource_group.test2.name + account_tier = "Standard" + account_replication_type = "LRS" +} + +// service principal of 'Microsoft.AzureStackHCI Resource Provider' +data "azuread_service_principal" "hciRp" { + client_id = "1412d89f-b8a8-4111-b4fd-e82905cbd85d" +} + +resource "azurerm_role_assignment" "MachineRoleAssign1" { + count = length(local.machine_roles) + scope = azurerm_resource_group.test2.id + role_definition_name = local.machine_roles[count.index] + principal_id = data.azurerm_arc_machine.server[0].identity[0].principal_id +} + +resource "azurerm_role_assignment" "MachineRoleAssign2" { + count = length(local.machine_roles) + scope = azurerm_resource_group.test2.id + role_definition_name = local.machine_roles[count.index] + principal_id = data.azurerm_arc_machine.server[1].identity[0].principal_id +} + +resource "azurerm_role_assignment" "ServicePrincipalRoleAssign" { + scope = azurerm_resource_group.test2.id + role_definition_name = "Azure Connected Machine Resource Manager" + principal_id = data.azuread_service_principal.hciRp.object_id +} + +resource "azurerm_stack_hci_cluster" "test" { + depends_on = [ + azurerm_key_vault_secret.DefaultARBApplication, + azurerm_key_vault_secret.AzureStackLCMUserCredential, + azurerm_key_vault_secret.LocalAdminCredential, + azurerm_key_vault_secret.WitnessStorageKey, + azurerm_role_assignment.ServicePrincipalRoleAssign, + azurerm_role_assignment.MachineRoleAssign1, + azurerm_role_assignment.MachineRoleAssign2, + ] + name = "hci${var.random_string}-cl" + resource_group_name = azurerm_resource_group.test2.name + location = azurerm_resource_group.test2.location + identity { + type = "SystemAssigned" + } + + // the client_id will be populated after deployment + lifecycle { + ignore_changes = [ + client_id + ] + } +} +`, data.Locations.Primary, data.RandomString, os.Getenv(imageIdEnv), os.Getenv(localAdminUserEnv), os.Getenv(localAdminUserPasswordEnv), os.Getenv(domainAdminUserEnv), os.Getenv(domainAdminUserPasswordEnv), os.Getenv(deploymentUserEnv), os.Getenv(deploymentUserPasswordEnv)) +} diff --git a/internal/services/azurestackhci/testdata/ad.ps1 b/internal/services/azurestackhci/testdata/ad.ps1 new file mode 100644 index 000000000000..5866cc49b1fc --- /dev/null +++ b/internal/services/azurestackhci/testdata/ad.ps1 @@ -0,0 +1,81 @@ +param( + $userName, + $password, + $authType, + $adouPath, + $ip, $port, + $domainFqdn, + $ifdeleteadou, + $deploymentUserName, + $deploymentUserPassword +) + +$script:ErrorActionPreference = 'Stop' +$count = 0 + +for ($count = 0; $count -lt 6; $count++) { + try { + $secpasswd = ConvertTo-SecureString $password -AsPlainText -Force + $domainShort = $domainFqdn.Split(".")[0] + $cred = New-Object System.Management.Automation.PSCredential -ArgumentList "$domainShort\$username", $secpasswd + + if ($authType -eq "CredSSP") { + try { + Enable-WSManCredSSP -Role Client -DelegateComputer $ip -Force + } + catch { + echo "Enable-WSManCredSSP failed" + } + } + + $session = New-PSSession -ComputerName $ip -Port $port -Authentication $authType -Credential $cred + if ($ifdeleteadou) { + Invoke-Command -Session $session -ScriptBlock { + $OUPrefixList = @("OU=Computers,", "OU=Users,", "") + foreach ($prefix in $OUPrefixList) { + $ouname = "$prefix$Using:adouPath" + echo "try to get OU: $ouname" + Try { + $ou = Get-ADOrganizationalUnit -Identity $ouname + } + Catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] { + $ou = $null + } + if ($ou) { + Set-ADOrganizationalUnit -Identity $ouname -ProtectedFromAccidentalDeletion $false + $ou | Remove-ADOrganizationalUnit -Recursive -Confirm:$False + echo "Deleted adou: $ouname" + } + } + } + + } + $deploymentSecPasswd = ConvertTo-SecureString $deploymentUserPassword -AsPlainText -Force + $lcmCred = New-Object System.Management.Automation.PSCredential -ArgumentList $deploymentUserName, $deploymentSecPasswd + Invoke-Command -Session $session -ScriptBlock { + echo "Install Nuget Provider" + Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Confirm:$false + echo "Install AsHciADArtifactsPreCreationTool" + Install-Module AsHciADArtifactsPreCreationTool -Repository PSGallery -Force -Confirm:$false + echo "Add KdsRootKey" + Add-KdsRootKey -EffectiveTime ((Get-Date).addhours(-10)) + echo "New HciAdObjectsPreCreation" + New-HciAdObjectsPreCreation -AzureStackLCMUserCredential $Using:lcmCred -AsHciOUName $Using:adouPath + } + break + } + catch { + echo "Error in retry ${count}:`n$_" + sleep 600 + } + finally { + if ($session) { + Remove-PSSession -Session $session + } + } +} + +if ($count -ge 3) { + throw "Failed to provision AD after 3 retries." +} + diff --git a/internal/services/azurestackhci/testdata/connect.ps1 b/internal/services/azurestackhci/testdata/connect.ps1 new file mode 100644 index 000000000000..bc5f4c438249 --- /dev/null +++ b/internal/services/azurestackhci/testdata/connect.ps1 @@ -0,0 +1,130 @@ +param( + $userName, + $password, + $authType, + $ip, $port, + $subscriptionId, $resourceGroupName, $region, $tenant, $servicePrincipalId, $servicePrincipalSecret, $expandC +) + +$script:ErrorActionPreference = 'Stop' +echo "Start to connect Arc server!" +$count = 0 + +if ($authType -eq "CredSSP") { + try { + Enable-WSManCredSSP -Role Client -DelegateComputer $ip -Force + } + catch { + echo "Enable-WSManCredSSP failed" + } +} +for ($count = 0; $count -lt 6; $count++) { + try { + $secpasswd = ConvertTo-SecureString $password -AsPlainText -Force + $cred = New-Object System.Management.Automation.PSCredential -ArgumentList ".\$username", $secpasswd + $session = New-PSSession -ComputerName $ip -Port $port -Authentication $authType -Credential $cred + + Invoke-Command -Session $session -ScriptBlock { + Param ($subscriptionId, $resourceGroupName, $region, $tenant, $servicePrincipalId, $servicePrincipalSecret) + $script:ErrorActionPreference = 'Stop' + + function Install-ModuleIfMissing { + param( + [Parameter(Mandatory = $true)] + [string]$Name, + [string]$Repository = 'PSGallery', + [switch]$Force, + [switch]$AllowClobber + ) + $script:ErrorActionPreference = 'Stop' + $module = Get-Module -Name $Name -ListAvailable + if (!$module) { + Write-Host "Installing module $Name" + Install-Module -Name $Name -Repository $Repository -Force:$Force -AllowClobber:$AllowClobber + } + } + + if ($expandC) { + # Expand C volume as much as possible + $drive_letter = "C" + $size = (Get-PartitionSupportedSize -DriveLetter $drive_letter) + if ($size.SizeMax -gt (Get-Partition -DriveLetter $drive_letter).Size) { + echo "Resizing volume" + Resize-Partition -DriveLetter $drive_letter -Size $size.SizeMax + } + } + + echo "Validate BITS is working" + $job = Start-BitsTransfer -Source https://aka.ms -Destination $env:TEMP -TransferType Download -Asynchronous + $count = 0 + while ($job.JobState -ne "Transferred" -and $count -lt 30) { + if ($job.JobState -eq "TransientError") { + throw "BITS transfer failed" + } + sleep 6 + $count++ + } + if ($count -ge 30) { + throw "BITS transfer failed after 3 minutes. Job state: $job.JobState" + } + + $creds = [System.Management.Automation.PSCredential]::new($servicePrincipalId, (ConvertTo-SecureString $servicePrincipalSecret -AsPlainText -Force)) + + Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Confirm:$false + + Install-ModuleIfMissing -Name Az -Repository PSGallery -Force + + Connect-AzAccount -Subscription $subscriptionId -Tenant $tenant -Credential $creds -ServicePrincipal + echo "login to Azure" + + Install-Module AzSHCI.ARCInstaller -Force -AllowClobber + Install-Module Az.StackHCI -Force -AllowClobber -RequiredVersion 2.2.3 + Install-Module AzStackHci.EnvironmentChecker -Repository PSGallery -Force -AllowClobber + Install-ModuleIfMissing Az.Accounts -Force -AllowClobber + Install-ModuleIfMissing Az.ConnectedMachine -Force -AllowClobber + Install-ModuleIfMissing Az.Resources -Force -AllowClobber + echo "Installed modules" + $id = (Get-AzContext).Tenant.Id + $token = (Get-AzAccessToken).Token + $accountid = (Get-AzContext).Account.Id + Invoke-AzStackHciArcInitialization -SubscriptionID $subscriptionId -ResourceGroup $resourceGroupName -TenantID $id -Region $region -Cloud "AzureCloud" -ArmAccessToken $token -AccountID $accountid + $exitCode = $LASTEXITCODE + $script:ErrorActionPreference = 'Stop' + if ($exitCode -eq 0) { + echo "Arc server connected!" + } + else { + throw "Arc server connection failed" + } + + sleep 600 + $ready = $false + while (!$ready) { + Connect-AzAccount -Subscription $subscriptionId -Tenant $tenant -Credential $creds -ServicePrincipal + $extension = Get-AzConnectedMachineExtension -Name "AzureEdgeLifecycleManager" -ResourceGroup $resourceGroupName -MachineName $env:COMPUTERNAME -SubscriptionId $subscriptionId + if ($extension.ProvisioningState -eq "Succeeded") { + $ready = $true + } + else { + echo "Waiting for LCM extension to be ready" + Start-Sleep -Seconds 30 + } + } + + } -ArgumentList $subscriptionId, $resourceGroupName, $region, $tenant, $servicePrincipalId, $servicePrincipalSecret + break + } + catch { + echo "Error in retry ${count}:`n$_" + sleep 600 + } + finally { + if ($session) { + Remove-PSSession -Session $session + } + } +} + +if ($count -ge 3) { + throw "Failed to connect Arc server after 3 retries." +} diff --git a/website/docs/r/stack_hci_deployment_setting.html.markdown b/website/docs/r/stack_hci_deployment_setting.html.markdown new file mode 100644 index 000000000000..783c6ac38793 --- /dev/null +++ b/website/docs/r/stack_hci_deployment_setting.html.markdown @@ -0,0 +1,585 @@ +--- +subcategory: "Azure Stack HCI" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_stack_hci_deployment_setting" +description: |- + Manages a Stack HCI Deployment Setting. +--- + +# azurerm_stack_hci_deployment_setting + +Manages a Stack HCI Deployment Setting. + +## Example Usage + +```hcl +provider "azuread" {} + +provider "azurerm" { + features {} +} + +variable "local_admin_user" { + description = "The username of the local administrator account." + sensitive = true + type = string +} + +variable "local_admin_password" { + description = "The password of the local administrator account." + sensitive = true + type = string +} + +variable "domain_admin_user" { + description = "The username of the domain account." + sensitive = true + type = string +} + +variable "domain_admin_password" { + description = "The password of the domain account." + sensitive = true + type = string +} + +variable "deployment_user" { + sensitive = true + type = string + description = "The username for deployment user." +} + +variable "deployment_user_password" { + sensitive = true + type = string + description = "The password for deployment user." +} + +locals { + servers = [ + { + name = "AzSHOST1", + ipv4Address = "192.168.1.12" + }, + { + name = "AzSHOST2", + ipv4Address = "192.168.1.13" + } + ] + connection_roles = [ + "Azure Connected Machine Onboarding", + "Azure Connected Machine Resource Administrator", + "Azure Resource Bridge Deployment Role" + ] + machine_roles = [ + "Key Vault Secrets User", + "Azure Connected Machine Resource Manager", + "Azure Stack HCI Device Management Role", + "Reader" + ] +} + +data "azurerm_resource_group" "example" { + name = "hci-example" +} + +resource "azuread_application" "example" { + display_name = "example-hci-onboard" +} + +# https://learn.microsoft.com/en-us/azure-stack/hci/deploy/deployment-azure-resource-manager-template#create-a-service-principal-and-client-secret +resource "azuread_service_principal" "example" { + client_id = azuread_application.example.client_id +} + +resource "azuread_service_principal_password" "example" { + service_principal_id = azuread_service_principal.example.object_id +} + +resource "azurerm_role_assignment" "connect" { + count = length(local.connection_roles) + scope = data.azurerm_resource_group.example.id + role_definition_name = local.connection_roles[count.index] + principal_id = azuread_service_principal.example.object_id +} + +## prepare Active Directory and register with Arc, and then the Arc VM is ready +data "azurerm_arc_machine" "server" { + count = length(local.servers) + name = local.servers[count.index].name + resource_group_name = data.azurerm_resource_group.example.name +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_key_vault" "DeploymentKeyVault" { + name = "hci-examplekv" + location = data.azurerm_resource_group.example.location + resource_group_name = data.azurerm_resource_group.example.name + enabled_for_deployment = true + enabled_for_template_deployment = true + enabled_for_disk_encryption = true + tenant_id = data.azurerm_client_config.current.tenant_id + soft_delete_retention_days = 30 + enable_rbac_authorization = true + public_network_access_enabled = true + sku_name = "standard" +} + +resource "azurerm_role_assignment" "KeyVault" { + scope = azurerm_key_vault.DeploymentKeyVault.id + role_definition_name = "Key Vault Secrets Officer" + principal_id = data.azurerm_client_config.current.object_id +} + +resource "azurerm_key_vault_secret" "AzureStackLCMUserCredential" { + name = "AzureStackLCMUserCredential" + content_type = "Secret" + value = base64encode("${var.deployment_user}:${var.deployment_user_password}") + key_vault_id = azurerm_key_vault.DeploymentKeyVault.id + depends_on = [azurerm_role_assignment.KeyVault] +} + +resource "azurerm_key_vault_secret" "LocalAdminCredential" { + name = "LocalAdminCredential" + content_type = "Secret" + value = base64encode("${var.local_admin_user}:${var.local_admin_password}") + key_vault_id = azurerm_key_vault.DeploymentKeyVault.id + depends_on = [azurerm_role_assignment.KeyVault] +} + +resource "azurerm_key_vault_secret" "DefaultARBApplication" { + name = "DefaultARBApplication" + content_type = "Secret" + value = base64encode("${azuread_service_principal.example.object_id}:${azuread_service_principal_password.example.value}") + key_vault_id = azurerm_key_vault.DeploymentKeyVault.id + depends_on = [azurerm_role_assignment.KeyVault] +} + +resource "azurerm_key_vault_secret" "WitnessStorageKey" { + name = "WitnessStorageKey" + content_type = "Secret" + value = base64encode(azurerm_storage_account.witness.primary_access_key) + key_vault_id = azurerm_key_vault.DeploymentKeyVault.id + depends_on = [azurerm_role_assignment.KeyVault] +} + +resource "azurerm_storage_account" "witness" { + name = "hciexamplesta" + location = data.azurerm_resource_group.example.location + resource_group_name = data.azurerm_resource_group.example.name + account_tier = "Standard" + account_replication_type = "LRS" +} + +// service principal of 'Microsoft.AzureStackHCI Resource Provider' application +data "azuread_service_principal" "hciRp" { + client_id = "1412d89f-b8a8-4111-b4fd-e82905cbd85d" +} + +resource "azurerm_role_assignment" "MachineRoleAssign1" { + count = length(local.machine_roles) + scope = data.azurerm_resource_group.example.id + role_definition_name = local.machine_roles[count.index] + principal_id = data.azurerm_arc_machine.server[0].identity[0].principal_id +} + +resource "azurerm_role_assignment" "MachineRoleAssign2" { + count = length(local.machine_roles) + scope = data.azurerm_resource_group.example.id + role_definition_name = local.machine_roles[count.index] + principal_id = data.azurerm_arc_machine.server[1].identity[0].principal_id +} + +resource "azurerm_role_assignment" "ServicePrincipalRoleAssign" { + scope = data.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_cluster" "example" { + depends_on = [ + azurerm_key_vault_secret.DefaultARBApplication, + azurerm_key_vault_secret.AzureStackLCMUserCredential, + azurerm_key_vault_secret.LocalAdminCredential, + azurerm_key_vault_secret.WitnessStorageKey, + azurerm_role_assignment.connect, + azurerm_role_assignment.ServicePrincipalRoleAssign, + azurerm_role_assignment.MachineRoleAssign1, + azurerm_role_assignment.MachineRoleAssign2, + ] + name = "hci-cl" + resource_group_name = data.azurerm_resource_group.example.name + location = data.azurerm_resource_group.example.location + identity { + type = "SystemAssigned" + } + + // the client_id will be populated after deployment + lifecycle { + ignore_changes = [ + client_id + ] + } +} + +resource "azurerm_stack_hci_deployment_setting" "example" { + stack_hci_cluster_id = azurerm_stack_hci_cluster.example.id + arc_resource_ids = [for server in data.azurerm_arc_machine.server : server.id] + version = "10.0.0.0" + + scale_unit { + adou_path = "OU=hci,DC=jumpstart,DC=local" + domain_fqdn = "jumpstart.local" + secrets_location = azurerm_key_vault.DeploymentKeyVault.vault_uri + naming_prefix = "hci" + + cluster { + azure_service_endpoint = "core.windows.net" + cloud_account_name = azurerm_storage_account.witness.name + name = azurerm_stack_hci_cluster.example.name + witness_type = "Cloud" + witness_path = "Cloud" + } + + host_network { + storage_auto_ip_enabled = true + storage_connectivity_switchless = false + intent { + name = "ManagementCompute" + override_adapter_property_enabled = false + override_qos_policy_enabled = false + override_virtual_switch_configuration_enabled = false + adapter = [ + "FABRIC", + "FABRIC2", + ] + traffic_type = [ + "Management", + "Compute", + ] + qos_policy_override { + priority_value8021_action_cluster = "7" + priority_value8021_action_smb = "3" + bandwidth_percentage_smb = "50" + } + adapter_property_override { + jumbo_packet = "9014" + network_direct = "Disabled" + network_direct_technology = "RoCEv2" + } + } + + intent { + name = "Storage" + override_adapter_property_enabled = false + override_qos_policy_enabled = false + override_virtual_switch_configuration_enabled = false + adapter = [ + "StorageA", + "StorageB", + ] + traffic_type = [ + "Storage", + ] + qos_policy_override { + priority_value8021_action_cluster = "7" + priority_value8021_action_smb = "3" + bandwidth_percentage_smb = "50" + } + adapter_property_override { + jumbo_packet = "9014" + network_direct = "Enabled" + network_direct_technology = "RoCEv2" + } + } + + storage_network { + name = "Storage1Network" + network_adapter_name = "StorageA" + vlan_id = "711" + } + + storage_network { + name = "Storage2Network" + network_adapter_name = "StorageB" + vlan_id = "712" + } + } + + infrastructure_network { + gateway = "192.168.1.1" + subnet_mask = "255.255.255.0" + dhcp_enabled = false + dns_server = [ + "192.168.1.254" + ] + ip_pool { + ending_address = "192.168.1.65" + starting_address = "192.168.1.55" + } + } + + optional_service { + custom_location = "customlocation" + } + + physical_node { + ipv4_address = "192.168.1.12" + name = "AzSHOST1" + } + + physical_node { + ipv4_address = "192.168.1.13" + name = "AzSHOST2" + } + + observability { + streaming_data_client_enabled = true + eu_location_enabled = false + episodic_data_upload_enabled = true + } + + security_setting { + bitlocker_boot_volume_enabled = true + bitlocker_data_volume_enabled = true + credential_guard_enabled = true + drift_control_enabled = true + drtm_protection_enabled = true + hvci_protection_enabled = true + side_channel_mitigation_enabled = true + smb_cluster_encryption_enabled = false + smb_signing_enabled = true + wdac_enabled = true + } + + storage { + configuration_mode = "Express" + } + } +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `stack_hci_cluster_id` - (Required) The ID of the Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `arc_resource_ids` - (Required) Specifies a list of IDs of Azure ARC machine resource to be part of cluster. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `scale_unit` - (Required) One or more `scale_unit` blocks as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `version` - (Required) The deployment template version, possible value is `10.0.0.0`. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + +A `adapter_property_override` block supports the following: + +* `jumbo_packet` - (Optional) The jumbo frame size of the adapter. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `network_direct` - (Optional) The network direct of the adapter. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `network_direct_technology` - (Optional) The network direct technology of the adapter. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + +A `cluster` block supports the following: + +* `azure_service_endpoint` - (Required) For Azure blob service endpoint type, select either Default or Custom domain. If you selected Custom domain, enter the domain for the blob service in this format `core.windows.net`. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `cloud_account_name` - (Required) Specify the Azure Storage account name for cloud witness for your Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `name` - (Required) The cluster name provided when preparing Active Directory. It must be 3-15 characters long and contain only letters, numbers and hyphens. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `witness_path` - (Required) Specify the fileshare path for the local witness for your Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `witness_type` - (Required) Use a cloud witness if you have internet access and if you use an Azure Storage account to provide a vote on cluster quorum. A cloud witness uses Azure Blob Storage to read or write a blob file and then uses it to arbitrate in split-brain resolution. Possible values are `Cloud`, `FileShare`. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + +A `host_network` block supports the following: + +* `intent` - (Required) One or more `intent` blocks as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `storage_network` - (Required) One or more `storage_network` blocks as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `storage_auto_ip_enabled` - (Optional) Whether allows users to specify IPs and Mask for Storage NICs when Network ATC is not assigning the IPs for storage automatically. Optional parameter required only for 3 Nodes Switchless deployments. Possible values are `true` and `false`. Defaults to `true`. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `storage_connectivity_switchless` - (Optional) Defines how the storage adapters between nodes are connected either switch or switch less. Possible values are `true` and `false`. Defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + +A `infrastructure_network` block supports the following: + +* `dns_server` - (Required) Specifies a list of IPv4 addresses of the DNS servers in your environment. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `gateway` - (Required) Specifies the default gateway that should be used for the provided IP address space. It should be in the format of an IPv4 IP address. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `ip_pool` - (Required) One or more `ip_pool` blocks as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `subnet_mask` - (Required) Specifies the subnet mask that matches the provided IP address space. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `dhcp_enabled` - (Optional) Whether allows customers to use DHCP for Hosts and Cluster IPs. If disabled, the deployment will Defaults to static IPs. If enabled, gateway and DNS servers are not required. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + +A `intent` block supports the following: + +* `name` - (Required) Specifies the name of the intent. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `adapter` - (Required) Specifies a list of ID of network interfaces used for the network intent. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `traffic_type` - (Required) Specifies a list of network traffic types. Possible values are `Compute`, `Storage`, `Management`. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `adapter_property_override` - (Optional) A `adapter_property_override` block as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `qos_policy_override` - (Optional) A `qos_policy_override` block as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `virtual_switch_configuration_override` - (Optional) A `virtual_switch_configuration_override` block as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `override_adapter_property_enabled` - (Optional) This parameter should only be modified based on your OEM guidance. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `override_qos_policy_enabled` - (Optional) This parameter should only be modified based on your OEM guidance. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `override_virtual_switch_configuration_enabled` - (Optional) This parameter should only be modified based on your OEM guidance. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + +A `ip_pool` block supports the following: + +* `ending_address` - (Required) Specifies starting IP address for the management network. A minimum of six free, contiguous IPv4 addresses (excluding your host IPs) are needed for infrastructure services such as clustering. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `starting_address` - (Required) Specifies ending IP address for the management network. A minimum of six free, contiguous IPv4 addresses (excluding your host IPs) are needed for infrastructure services such as clustering. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + +A `observability` block supports the following: + +* `episodic_data_upload_enabled` - (Required) Whether to collect log data to facilitate quicker issue resolution. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `eu_location_enabled` - (Required) Whether to store data sent to Microsoft in EU. The log and diagnostic data is sent to the appropriate diagnostics servers depending upon where your cluster resides. Setting this to `false` results in all data sent to Microsoft to be stored outside of the EU. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `streaming_data_client_enabled` - (Required) Whether send telemetry data to Microsoft. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + +A `optional_service` block supports the following: + +* `custom_location` - (Required) Specifies the name of custom location. A custom location will be created after the deployment is completed. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + +A `physical_node` block supports the following: + +* `ipv4_address` - (Required) Specifies the IPv4 address assigned to each physical server on your Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `name` - (Required) The NETBIOS name of each physical server on your Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + +A `qos_policy_override` block supports the following: + +* `bandwidth_percentage_smb` - (Optional) Specifies the bandwidth allocation in % for the storage traffic. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `priority_value8021_action_cluster` - (Optional) Specifies the Cluster traffic priority. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `priority_value8021_action_smb` - (Optional) Specifies the Priority Flow Control where Data Center Bridging (DCB) is used. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + +A `scale_unit` block supports the following: + +* `adou_path` - (Required) Specify the full name of the Active Directory Organizational Unit container object prepared for the deployment, including the domain components. For example:`OU=HCI01,DC=contoso,DC=com`. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `cluster` - (Required) A `cluster` block as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `domain_fqdn` - (Required) Specifies the FQDN to deploy cluster. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `host_network` - (Required) A `host_network` block as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `infrastructure_network` - (Required) One or more `infrastructure_network` blocks as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `naming_prefix` - (Required) Specifies the naming prefix to deploy cluster. It must be 1-8 characters long and contain only letters, numbers and hyphens Changing this forces a new Stack HCI Deployment Setting to be created. + +* `optional_service` - (Required) A `optional_service` block as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `physical_node` - (Required) One or more `physical_node` blocks as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `secrets_location` - (Required) The URI to the keyvault or secret store. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `security_setting` - (Required) A `security_setting` block as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `storage` - (Required) A `storage` block as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `observability` - (Required) A `observability` block as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + +A `security_setting` block supports the following: + +* `bitlocker_boot_volume_enabled` - (Required) Whether to enable BitLocker for boot volume. When set to `true`, BitLocker XTS_AES 256-bit encryption is enabled for all data-at-rest on the OS volume of your Azure Stack HCI cluster. This setting is TPM-hardware dependent. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `bitlocker_data_volume_enabled` - (Required) Whether to enable BitLocker for data volume. When set to `true`, BitLocker XTS-AES 256-bit encryption is enabled for all data-at-rest on your Azure Stack HCI cluster shared volumes. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `credential_guard_enabled` - (Required) Whether to enable credential guard. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `drift_control_enabled` - (Required) Whether to enable drift control. When set to `true`, the security baseline is re-applied regularly. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `drtm_protection_enabled` - (Required) Whether to enable DRTM protection. When set to `true`, Secure Boot is enabled on your Azure HCI cluster. This setting is hardware dependent. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `hvci_protection_enabled` - (Required) Whether to enable HVCI protection. When set to `true`, Hypervisor-protected Code Integrity is enabled on your Azure HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `side_channel_mitigation_enabled` - (Required) Whether to enable side channel mitigation. When set to `true`, all side channel mitigations are enabled on your Azure HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `smb_cluster_encryption_enabled` - (Required) Whether to enable SMB cluster encryption. When set to `true`, cluster east-west traffic is encrypted. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `smb_signing_enabled` - (Required) Whether to enable SMB signing. When set to `true`, the SMB default instance requires sign in for the client and server services. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `wdac_enabled` - (Required) Whether to enable WDAC. When set to `true`, applications and the code that you can run on your Azure Stack HCI cluster are limited. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + +A `storage` block supports the following: + +* `configuration_mode` - (Required) The configuration mode of storage. If set to `Express` and your storage is configured as per best practices based on the number of nodes in the cluster. Possible values are `Express`, `InfraOnly` and `KeepStorage`. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + +A `storage_network` block supports the following: + +* `name` - (Required) The name of the storage network. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `network_adapter_name` - (Required) The name of the network adapter. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `vlan_id` - (Required) Specifies the ID for the VLAN storage network. This setting is applied to the network interfaces that route the storage and VM migration traffic. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + +A `virtual_switch_configuration_override` block supports the following: + +* `enable_iov` - (Optional) Specifies the IoV enable status for Virtual Switch. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `load_balancing_algorithm` - (Optional) Specifies the load balancing algorithm for Virtual Switch. Changing this forces a new Stack HCI Deployment Setting to be created. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Stack HCI Deployment Setting. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +* `create` - (Defaults to 6 hours) Used when creating the Stack HCI Deployment Setting. +* `read` - (Defaults to 5 minutes) Used when retrieving the Stack HCI Deployment Setting. +* `delete` - (Defaults to 1 hour) Used when deleting the Stack HCI Deployment Setting. + +## Import + +Stack HCI Deployment Settings can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_stack_hci_deployment_setting.example /subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.AzureStackHCI/clusters/clus1/deploymentSettings/default +``` From 1f59032c3f792c78eda255cdee751bfe2e3be8e7 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Sat, 11 May 2024 02:36:47 +0000 Subject: [PATCH 02/16] delete customlocation & arc bridge --- internal/clients/client.go | 5 + internal/features/defaults.go | 4 + internal/features/user_flags.go | 6 + internal/provider/features.go | 37 +- internal/provider/features_test.go | 24 + .../stack_hci_deployment_setting_resource.go | 493 ++++++++++-------- ...ck_hci_deployment_setting_resource_test.go | 129 +++-- .../services/azurestackhci/testdata/ad.ps1 | 2 + .../azurestackhci/testdata/connect.ps1 | 2 + .../extendedlocation/client/client.go | 26 + .../extendedlocation/2021-08-15/client.go | 28 + vendor/modules.txt | 1 + .../docs/guides/features-block.html.markdown | 15 + ...stack_hci_deployment_setting.html.markdown | 146 +++--- 14 files changed, 566 insertions(+), 352 deletions(-) create mode 100644 internal/services/extendedlocation/client/client.go create mode 100644 vendor/github.com/hashicorp/go-azure-sdk/resource-manager/extendedlocation/2021-08-15/client.go diff --git a/internal/clients/client.go b/internal/clients/client.go index d280edd1ec2d..5aa21a0a9502 100644 --- a/internal/clients/client.go +++ b/internal/clients/client.go @@ -73,6 +73,7 @@ import ( elasticsan "github.com/hashicorp/terraform-provider-azurerm/internal/services/elasticsan/client" eventgrid "github.com/hashicorp/terraform-provider-azurerm/internal/services/eventgrid/client" eventhub "github.com/hashicorp/terraform-provider-azurerm/internal/services/eventhub/client" + extendedlocation "github.com/hashicorp/terraform-provider-azurerm/internal/services/extendedlocation/client" fluidrelay "github.com/hashicorp/terraform-provider-azurerm/internal/services/fluidrelay/client" frontdoor "github.com/hashicorp/terraform-provider-azurerm/internal/services/frontdoor/client" graph "github.com/hashicorp/terraform-provider-azurerm/internal/services/graphservices/client" @@ -210,6 +211,7 @@ type Client struct { ElasticSan *elasticsan.Client EventGrid *eventgrid_v2022_06_15.Client Eventhub *eventhub.Client + ExtendedLocation *extendedlocation.Client FluidRelay *fluidrelay_2022_05_26.Client Frontdoor *frontdoor.Client Graph *graph.Client @@ -447,6 +449,9 @@ func (client *Client) Build(ctx context.Context, o *common.ClientOptions) error if client.Eventhub, err = eventhub.NewClient(o); err != nil { return fmt.Errorf("building clients for Eventhub: %+v", err) } + if client.ExtendedLocation, err = extendedlocation.NewClient(o); err != nil { + return fmt.Errorf("building clients for ExtendedLocation: %+v", err) + } if client.FluidRelay, err = fluidrelay.NewClient(o); err != nil { return fmt.Errorf("building clients for FluidRelay: %+v", err) } diff --git a/internal/features/defaults.go b/internal/features/defaults.go index 4bb9b38a5133..151cb743ad2f 100644 --- a/internal/features/defaults.go +++ b/internal/features/defaults.go @@ -17,6 +17,10 @@ func Default() UserFeatures { ApplicationInsights: ApplicationInsightFeatures{ DisableGeneratedRule: false, }, + AzureStackHci: AzureStackHciFeatures{ + DeleteArcBridgeOnDestroy: false, + DeleteCustomLocationOnDestroy: false, + }, CognitiveAccount: CognitiveAccountFeatures{ PurgeSoftDeleteOnDestroy: true, }, diff --git a/internal/features/user_flags.go b/internal/features/user_flags.go index f696f5cfeb6c..7bcd3e7b3283 100644 --- a/internal/features/user_flags.go +++ b/internal/features/user_flags.go @@ -7,6 +7,7 @@ type UserFeatures struct { ApiManagement ApiManagementFeatures AppConfiguration AppConfigurationFeatures ApplicationInsights ApplicationInsightFeatures + AzureStackHci AzureStackHciFeatures CognitiveAccount CognitiveAccountFeatures VirtualMachine VirtualMachineFeatures VirtualMachineScaleSet VirtualMachineScaleSetFeatures @@ -71,6 +72,11 @@ type ApplicationInsightFeatures struct { DisableGeneratedRule bool } +type AzureStackHciFeatures struct { + DeleteArcBridgeOnDestroy bool + DeleteCustomLocationOnDestroy bool +} + type ManagedDiskFeatures struct { ExpandWithoutDowntime bool } diff --git a/internal/provider/features.go b/internal/provider/features.go index 57238f1db26d..9409440985c4 100644 --- a/internal/provider/features.go +++ b/internal/provider/features.go @@ -15,7 +15,7 @@ func schemaFeatures(supportLegacyTestSuite bool) *pluginsdk.Schema { // NOTE: if there's only one nested field these want to be Required (since there's no point // specifying the block otherwise) - however for 2+ they should be optional featuresMap := map[string]*pluginsdk.Schema{ - //lintignore:XS003 + // lintignore:XS003 "api_management": { Type: pluginsdk.TypeList, Optional: true, @@ -73,6 +73,26 @@ func schemaFeatures(supportLegacyTestSuite bool) *pluginsdk.Schema { }, }, + "azure_stack_hci": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "delete_arc_bridge_on_destroy": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + "delete_custom_location_on_destroy": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + }, + }, + }, + "cognitive_account": { Type: pluginsdk.TypeList, Optional: true, @@ -189,7 +209,7 @@ func schemaFeatures(supportLegacyTestSuite bool) *pluginsdk.Schema { }, }, - //lintignore:XS003 + // lintignore:XS003 "virtual_machine": { Type: pluginsdk.TypeList, Optional: true, @@ -409,6 +429,19 @@ func expandFeatures(input []interface{}) features.UserFeatures { } } + if raw, ok := val["azure_stack_hci"]; ok { + items := raw.([]interface{}) + if len(items) > 0 && items[0] != nil { + azureStackHciRaw := items[0].(map[string]interface{}) + if v, ok := azureStackHciRaw["delete_arc_bridge_on_destroy"]; ok { + featuresMap.AzureStackHci.DeleteArcBridgeOnDestroy = v.(bool) + } + if v, ok := azureStackHciRaw["delete_custom_location_on_destroy"]; ok { + featuresMap.AzureStackHci.DeleteCustomLocationOnDestroy = v.(bool) + } + } + } + if raw, ok := val["cognitive_account"]; ok { items := raw.([]interface{}) if len(items) > 0 && items[0] != nil { diff --git a/internal/provider/features_test.go b/internal/provider/features_test.go index 3714c590c6c7..35d9c8a19101 100644 --- a/internal/provider/features_test.go +++ b/internal/provider/features_test.go @@ -32,6 +32,10 @@ func TestExpandFeatures(t *testing.T) { ApplicationInsights: features.ApplicationInsightFeatures{ DisableGeneratedRule: false, }, + AzureStackHci: features.AzureStackHciFeatures{ + DeleteArcBridgeOnDestroy: false, + DeleteCustomLocationOnDestroy: false, + }, CognitiveAccount: features.CognitiveAccountFeatures{ PurgeSoftDeleteOnDestroy: true, }, @@ -105,6 +109,12 @@ func TestExpandFeatures(t *testing.T) { "disable_generated_rule": true, }, }, + "azure_stack_hci": []interface{}{ + map[string]interface{}{ + "delete_arc_bridge_on_destroy": true, + "delete_custom_location_on_destroy": true, + }, + }, "cognitive_account": []interface{}{ map[string]interface{}{ "purge_soft_delete_on_destroy": true, @@ -193,6 +203,10 @@ func TestExpandFeatures(t *testing.T) { ApplicationInsights: features.ApplicationInsightFeatures{ DisableGeneratedRule: true, }, + AzureStackHci: features.AzureStackHciFeatures{ + DeleteArcBridgeOnDestroy: true, + DeleteCustomLocationOnDestroy: true, + }, CognitiveAccount: features.CognitiveAccountFeatures{ PurgeSoftDeleteOnDestroy: true, }, @@ -266,6 +280,12 @@ func TestExpandFeatures(t *testing.T) { "disable_generated_rule": false, }, }, + "azure_stack_hci": []interface{}{ + map[string]interface{}{ + "delete_arc_bridge_on_destroy": false, + "delete_custom_location_on_destroy": false, + }, + }, "cognitive_account": []interface{}{ map[string]interface{}{ "purge_soft_delete_on_destroy": false, @@ -354,6 +374,10 @@ func TestExpandFeatures(t *testing.T) { ApplicationInsights: features.ApplicationInsightFeatures{ DisableGeneratedRule: false, }, + AzureStackHci: features.AzureStackHciFeatures{ + DeleteArcBridgeOnDestroy: false, + DeleteCustomLocationOnDestroy: false, + }, CognitiveAccount: features.CognitiveAccountFeatures{ PurgeSoftDeleteOnDestroy: false, }, diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go index f984d9948369..a4068780aa73 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go @@ -4,13 +4,18 @@ import ( "context" "fmt" "regexp" + "strings" "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/azurestackhci/2024-01-01/deploymentsettings" + "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/storagecontainers" + "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/resourceconnector/2022-10-27/appliances" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -42,12 +47,27 @@ type ScaleUnitModel struct { HostNetwork []HostNetworkModel `tfschema:"host_network"` InfrastructureNetwork []InfrastructureNetworkModel `tfschema:"infrastructure_network"` NamingPrefix string `tfschema:"naming_prefix"` - Observability []ObservabilityModel `tfschema:"observability"` OptionalService []OptionalServiceModel `tfschema:"optional_service"` PhysicalNode []PhysicalNodeModel `tfschema:"physical_node"` SecretsLocation string `tfschema:"secrets_location"` - SecuritySetting []SecuritySettingModel `tfschema:"security_setting"` Storage []StorageModel `tfschema:"storage"` + + // flatten 'observability' block, for API always return them + StreamingDataClientEnabled bool `tfschema:"streaming_data_client_enabled"` + EuLocationEnabled bool `tfschema:"eu_location_enabled"` + EpisodicDataUploadEnabled bool `tfschema:"episodic_data_upload_enabled"` + + // flatten 'securitySetting' block, for API always return them + BitlockerBootVolumeEnabled bool `tfschema:"bitlocker_boot_volume_enabled"` + BitlockerDataVolumeEnabled bool `tfschema:"bitlocker_data_volume_enabled"` + CredentialGuardEnabled bool `tfschema:"credential_guard_enabled"` + DriftControlEnabled bool `tfschema:"drift_control_enabled"` + DrtmProtectionEnabled bool `tfschema:"drtm_protection_enabled"` + HvciProtectionEnabled bool `tfschema:"hvci_protection_enabled"` + SideChannelMitigationEnabled bool `tfschema:"side_channel_mitigation_enabled"` + SmbSigningEnabled bool `tfschema:"smb_signing_enabled"` + SmbClusterEncryptionEnabled bool `tfschema:"smb_cluster_encryption_enabled"` + WdacEnabled bool `tfschema:"wdac_enabled"` } type ClusterModel struct { @@ -59,37 +79,37 @@ type ClusterModel struct { } type HostNetworkModel struct { - Intent []HostNetworkIntentModel `tfschema:"intent"` - StorageAutoIpEnabled bool `tfschema:"storage_auto_ip_enabled"` - StorageConnectivitySwitchless bool `tfschema:"storage_connectivity_switchless"` - StorageNetwork []HostNetworkStorageNetworkModel `tfschema:"storage_network"` + Intent []HostNetworkIntentModel `tfschema:"intent"` + StorageAutoIpEnabled bool `tfschema:"storage_auto_ip_enabled"` + StorageConnectivitySwitchlessEnabled bool `tfschema:"storage_connectivity_switchless_enabled"` + StorageNetwork []HostNetworkStorageNetworkModel `tfschema:"storage_network"` } type HostNetworkIntentModel struct { Adapter []string `tfschema:"adapter"` - AdapterPropertyOverride []HostNetworkIntentAdapterPropertyOverrideModel `tfschema:"adapter_property_override"` Name string `tfschema:"name"` + OverrideAdapterProperty []OverrideHostNetworkIntentAdapterPropertyModel `tfschema:"override_adapter_property"` OverrideAdapterPropertyEnabled bool `tfschema:"override_adapter_property_enabled"` + OverrideQosPolicy []OverrideHostNetworkIntentQosPolicyModel `tfschema:"override_qos_policy"` OverrideQosPolicyEnabled bool `tfschema:"override_qos_policy_enabled"` + OverrideVirtualSwitchConfiguration []OverrideHostNetworkIntentVirtualSwitchConfigurationModel `tfschema:"override_virtual_switch_configuration"` OverrideVirtualSwitchConfigurationEnabled bool `tfschema:"override_virtual_switch_configuration_enabled"` - QosPolicyOverride []HostNetworkIntentQosPolicyOverrideModel `tfschema:"qos_policy_override"` TrafficType []string `tfschema:"traffic_type"` - VirtualSwitchConfigurationOverride []HostNetworkIntentVirtualSwitchConfigurationOverrideModel `tfschema:"virtual_switch_configuration_override"` } -type HostNetworkIntentAdapterPropertyOverrideModel struct { +type OverrideHostNetworkIntentAdapterPropertyModel struct { JumboPacket string `tfschema:"jumbo_packet"` NetworkDirect string `tfschema:"network_direct"` NetworkDirectTechnology string `tfschema:"network_direct_technology"` } -type HostNetworkIntentQosPolicyOverrideModel struct { +type OverrideHostNetworkIntentQosPolicyModel struct { BandWidthPercentageSMB string `tfschema:"bandwidth_percentage_smb"` PriorityValue8021ActionCluster string `tfschema:"priority_value8021_action_cluster"` PriorityValue8021ActionSMB string `tfschema:"priority_value8021_action_smb"` } -type HostNetworkIntentVirtualSwitchConfigurationOverrideModel struct { +type OverrideHostNetworkIntentVirtualSwitchConfigurationModel struct { EnableIov string `tfschema:"enable_iov"` LoadBalancingAlgorithm string `tfschema:"load_balancing_algorithm"` } @@ -113,12 +133,6 @@ type IpPoolModel struct { EndingAddress string `tfschema:"ending_address"` } -type ObservabilityModel struct { - StreamingDataClientEnabled bool `tfschema:"streaming_data_client_enabled"` - EuLocationEnabled bool `tfschema:"eu_location_enabled"` - EpisodicDataUploadEnabled bool `tfschema:"episodic_data_upload_enabled"` -} - type OptionalServiceModel struct { CustomLocation string `tfschema:"custom_location"` } @@ -128,19 +142,6 @@ type PhysicalNodeModel struct { Ipv4Address string `tfschema:"ipv4_address"` } -type SecuritySettingModel struct { - BitlockerBootVolumeEnabled bool `tfschema:"bitlocker_boot_volume_enabled"` - BitlockerDataVolumeEnabled bool `tfschema:"bitlocker_data_volume_enabled"` - CredentialGuardEnabled bool `tfschema:"credential_guard_enabled"` - DriftControlEnabled bool `tfschema:"drift_control_enabled"` - DrtmProtectionEnabled bool `tfschema:"drtm_protection_enabled"` - HvciProtectionEnabled bool `tfschema:"hvci_protection_enabled"` - SideChannelMitigationEnabled bool `tfschema:"side_channel_mitigation_enabled"` - SmbSigningEnabled bool `tfschema:"smb_signing_enabled"` - SmbClusterEncryptionEnabled bool `tfschema:"smb_cluster_encryption_enabled"` - WdacEnabled bool `tfschema:"wdac_enabled"` -} - type StorageModel struct { ConfigurationMode string `tfschema:"configuration_mode"` } @@ -169,7 +170,7 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem ForceNew: true, ValidateFunc: validation.StringMatch( regexp.MustCompile(`^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$`), - "must be in format `10.0.0.1`", + "the version must be a set of numbers separated by dots: `10.0.0.1`", ), }, @@ -200,7 +201,7 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem ForceNew: true, ValidateFunc: validation.StringMatch( regexp.MustCompile("^[a-zA-Z0-9-]{3,15}$"), - "must be 3-15 characters long and contain only letters, numbers and hyphens", + "the cluster name must be 3-15 characters long and contain only letters, numbers and hyphens", ), }, @@ -259,6 +260,13 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem MinItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "adapter": { Type: pluginsdk.TypeList, Required: true, @@ -270,13 +278,6 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem }, }, - "name": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - "traffic_type": { Type: pluginsdk.TypeList, Required: true, @@ -292,7 +293,7 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem }, }, - "adapter_property_override": { + "override_adapter_property": { Type: pluginsdk.TypeList, Optional: true, ForceNew: true, @@ -327,21 +328,10 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem Type: pluginsdk.TypeBool, Optional: true, ForceNew: true, + Default: false, }, - "override_qos_policy_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - ForceNew: true, - }, - - "override_virtual_switch_configuration_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - ForceNew: true, - }, - - "qos_policy_override": { + "override_qos_policy": { Type: pluginsdk.TypeList, Optional: true, ForceNew: true, @@ -372,7 +362,14 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem }, }, - "virtual_switch_configuration_override": { + "override_qos_policy_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, + + "override_virtual_switch_configuration": { Type: pluginsdk.TypeList, Optional: true, ForceNew: true, @@ -395,6 +392,13 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem }, }, }, + + "override_virtual_switch_configuration_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + ForceNew: true, + Default: false, + }, }, }, }, @@ -437,7 +441,7 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem Default: true, }, - "storage_connectivity_switchless": { + "storage_connectivity_switchless_enabled": { Type: pluginsdk.TypeBool, Optional: true, ForceNew: true, @@ -507,6 +511,7 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem Type: pluginsdk.TypeBool, Optional: true, ForceNew: true, + Default: false, }, }, }, @@ -518,7 +523,7 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem ForceNew: true, ValidateFunc: validation.StringMatch( regexp.MustCompile("^[a-zA-Z0-9-]{1,8}$"), - "must be 1-8 characters long and contain only letters, numbers and hyphens", + "the naming prefix must be 1-8 characters long and contain only letters, numbers and hyphens", ), }, @@ -591,102 +596,95 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem }, }, - "observability": { - Type: pluginsdk.TypeList, - Required: true, + "streaming_data_client_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, ForceNew: true, - MaxItems: 1, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "streaming_data_client_enabled": { - Type: pluginsdk.TypeBool, - Required: true, - ForceNew: true, - }, + }, - "eu_location_enabled": { - Type: pluginsdk.TypeBool, - Required: true, - ForceNew: true, - }, + "eu_location_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + }, - "episodic_data_upload_enabled": { - Type: pluginsdk.TypeBool, - Required: true, - ForceNew: true, - }, - }, - }, + "episodic_data_upload_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + ForceNew: true, }, - "security_setting": { - Type: pluginsdk.TypeList, - Required: true, + "bitlocker_boot_volume_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, ForceNew: true, - MaxItems: 1, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "bitlocker_boot_volume_enabled": { - Type: pluginsdk.TypeBool, - Required: true, - ForceNew: true, - }, + }, - "bitlocker_data_volume_enabled": { - Type: pluginsdk.TypeBool, - Required: true, - ForceNew: true, - }, + "bitlocker_data_volume_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + ForceNew: true, + }, - "credential_guard_enabled": { - Type: pluginsdk.TypeBool, - Required: true, - ForceNew: true, - }, + "credential_guard_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + }, - "drift_control_enabled": { - Type: pluginsdk.TypeBool, - Required: true, - ForceNew: true, - }, + "drift_control_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + ForceNew: true, + }, - "drtm_protection_enabled": { - Type: pluginsdk.TypeBool, - Required: true, - ForceNew: true, - }, + "drtm_protection_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + ForceNew: true, + }, - "hvci_protection_enabled": { - Type: pluginsdk.TypeBool, - Required: true, - ForceNew: true, - }, + "hvci_protection_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + ForceNew: true, + }, - "side_channel_mitigation_enabled": { - Type: pluginsdk.TypeBool, - Required: true, - ForceNew: true, - }, + "side_channel_mitigation_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + ForceNew: true, + }, - "smb_signing_enabled": { - Type: pluginsdk.TypeBool, - Required: true, - ForceNew: true, - }, + "smb_signing_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + ForceNew: true, + }, - "smb_cluster_encryption_enabled": { - Type: pluginsdk.TypeBool, - Required: true, - ForceNew: true, - }, + "smb_cluster_encryption_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + }, - "wdac_enabled": { - Type: pluginsdk.TypeBool, - Required: true, - ForceNew: true, - }, - }, - }, + "wdac_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + ForceNew: true, }, }, }, @@ -734,7 +732,7 @@ func (r StackHCIDeploymentSettingResource) Create() sdk.ResourceFunc { }, } - // the resource exists even validation error + // the resource may exist even validation error metadata.SetID(id) // do validation @@ -801,10 +799,71 @@ func (StackHCIDeploymentSettingResource) Delete() sdk.ResourceFunc { return err } + resp, err := client.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(id) + } + + return fmt.Errorf("retrieving %s: %+v", id, err) + } + if err := client.DeleteThenPoll(ctx, *id); err != nil { return fmt.Errorf("deleting %s: %+v", id, err) } + if metadata.Client.Features.AzureStackHci.DeleteCustomLocationOnDestroy { + var customLocationName string + if resp.Model != nil && resp.Model.Properties != nil && + len(resp.Model.Properties.DeploymentConfiguration.ScaleUnits) > 0 && + resp.Model.Properties.DeploymentConfiguration.ScaleUnits[0].DeploymentData.OptionalServices != nil { + customLocationName = pointer.From(resp.Model.Properties.DeploymentConfiguration.ScaleUnits[0].DeploymentData.OptionalServices.CustomLocation) + } + + // try to delete the Custom Location generated during deployment + if customLocationName != "" { + customLocationId := customlocations.NewCustomLocationID(id.SubscriptionId, id.ResourceGroupName, customLocationName) + + // try to delete the HCI Staroge Containers generated during deployment in the Custom Location + storageContainerClient := metadata.Client.AzureStackHCI.StorageContainers + resourceGroupId := commonids.NewResourceGroupID(id.SubscriptionId, id.ResourceGroupName) + storageContainers, err := storageContainerClient.ListComplete(ctx, resourceGroupId) + if err != nil { + return fmt.Errorf("retrieving Stack HCI Storage Containers under %s: %+v", resourceGroupId.ID(), err) + } + + // find all Storage Containers under the Custom Location + storageContainerNamePattern := regexp.MustCompile(`UserStorage[0-9]+-[a-z0-9]{32}`) + for _, v := range storageContainers.Items { + if v.Id != nil && v.ExtendedLocation != nil && v.ExtendedLocation.Name != nil && strings.EqualFold(*v.ExtendedLocation.Name, customLocationId.ID()) && v.Name != nil && storageContainerNamePattern.Match([]byte(*v.Name)) { + storageContainerId, err := storagecontainers.ParseStorageContainerIDInsensitively(*v.Id) + if err != nil { + return err + } + + if err := storageContainerClient.DeleteThenPoll(ctx, *storageContainerId); err != nil { + return err + } + } + } + + customLocationsClient := metadata.Client.ExtendedLocation.CustomLocations + if err := customLocationsClient.DeleteThenPoll(ctx, customLocationId); err != nil { + return fmt.Errorf("deleting %s: %+v", customLocationId, err) + } + } + } + + if metadata.Client.Features.AzureStackHci.DeleteArcBridgeOnDestroy { + // try to delete the Arc Resource Bridge Appliance generated during deployment + applianceName := fmt.Sprintf("%s-arcbridge", id.ClusterName) + applianceId := appliances.NewApplianceID(id.SubscriptionId, id.ResourceGroupName, applianceName) + applianceClient := metadata.Client.ArcResourceBridge.AppliancesClient + if err := applianceClient.DeleteThenPoll(ctx, applianceId); err != nil { + return fmt.Errorf("deleting %s: %+v", applianceId, err) + } + } + return nil }, } @@ -825,11 +884,11 @@ func ExpandDeploymentSettingScaleUnits(input []ScaleUnitModel) []deploymentsetti HostNetwork: ExpandDeploymentSettingHostNetwork(item.HostNetwork), InfrastructureNetwork: ExpandDeploymentSettingInfrastructureNetwork(item.InfrastructureNetwork), NamingPrefix: pointer.To(item.NamingPrefix), - Observability: ExpandDeploymentSettingObservability(item.Observability), + Observability: ExpandDeploymentSettingObservability(item), OptionalServices: ExpandDeploymentSettingOptionalService(item.OptionalService), PhysicalNodes: ExpandDeploymentSettingPhysicalNode(item.PhysicalNode), SecretsLocation: pointer.To(item.SecretsLocation), - SecuritySettings: ExpandDeploymentSettingSecuritySetting(item.SecuritySetting), + SecuritySettings: ExpandDeploymentSettingSecuritySetting(item), Storage: ExpandDeploymentSettingStorage(item.Storage), }, }) @@ -845,20 +904,39 @@ func FlattenDeploymentSettingScaleUnits(input []deploymentsettings.ScaleUnits) [ results := make([]ScaleUnitModel, 0, len(input)) for _, item := range input { - results = append(results, ScaleUnitModel{ + result := ScaleUnitModel{ AdouPath: pointer.From(item.DeploymentData.AdouPath), Cluster: FlattenDeploymentSettingCluster(item.DeploymentData.Cluster), DomainFqdn: pointer.From(item.DeploymentData.DomainFqdn), HostNetwork: FlattenDeploymentSettingHostNetwork(item.DeploymentData.HostNetwork), InfrastructureNetwork: FlattenDeploymentSettingInfrastructureNetwork(item.DeploymentData.InfrastructureNetwork), NamingPrefix: pointer.From(item.DeploymentData.NamingPrefix), - Observability: FlattenDeploymentSettingObservability(item.DeploymentData.Observability), OptionalService: FlattenDeploymentSettingOptionalService(item.DeploymentData.OptionalServices), PhysicalNode: FlattenDeploymentSettingPhysicalNode(item.DeploymentData.PhysicalNodes), SecretsLocation: pointer.From(item.DeploymentData.SecretsLocation), - SecuritySetting: FlattenDeploymentSettingSecuritySetting(item.DeploymentData.SecuritySettings), Storage: FlattenDeploymentSettingStorage(item.DeploymentData.Storage), - }) + } + + if observability := item.DeploymentData.Observability; observability != nil { + result.EpisodicDataUploadEnabled = pointer.From(observability.EpisodicDataUpload) + result.EuLocationEnabled = pointer.From(observability.EuLocation) + result.StreamingDataClientEnabled = pointer.From(observability.StreamingDataClient) + } + + if securitySettings := item.DeploymentData.SecuritySettings; securitySettings != nil { + result.BitlockerBootVolumeEnabled = pointer.From(securitySettings.BitlockerBootVolume) + result.BitlockerDataVolumeEnabled = pointer.From(securitySettings.BitlockerDataVolumes) + result.CredentialGuardEnabled = pointer.From(securitySettings.CredentialGuardEnforced) + result.DriftControlEnabled = pointer.From(securitySettings.DriftControlEnforced) + result.DrtmProtectionEnabled = pointer.From(securitySettings.DrtmProtection) + result.HvciProtectionEnabled = pointer.From(securitySettings.HvciProtection) + result.SideChannelMitigationEnabled = pointer.From(securitySettings.SideChannelMitigationEnforced) + result.SmbClusterEncryptionEnabled = pointer.From(securitySettings.SmbClusterEncryption) + result.SmbSigningEnabled = pointer.From(securitySettings.SmbSigningEnforced) + result.WdacEnabled = pointer.From(securitySettings.WdacEnforced) + } + + results = append(results, result) } return results @@ -904,7 +982,7 @@ func ExpandDeploymentSettingHostNetwork(input []HostNetworkModel) *deploymentset return &deploymentsettings.HostNetwork{ Intents: ExpandDeploymentSettingHostNetworkIntent(v.Intent), EnableStorageAutoIP: pointer.To(v.StorageAutoIpEnabled), - StorageConnectivitySwitchless: pointer.To(v.StorageConnectivitySwitchless), + StorageConnectivitySwitchless: pointer.To(v.StorageConnectivitySwitchlessEnabled), StorageNetworks: ExpandDeploymentSettingHostNetworkStorageNetwork(v.StorageNetwork), } } @@ -915,10 +993,10 @@ func FlattenDeploymentSettingHostNetwork(input *deploymentsettings.HostNetwork) } return []HostNetworkModel{{ - Intent: FlattenDeploymentSettingHostNetworkIntent(input.Intents), - StorageAutoIpEnabled: pointer.From(input.EnableStorageAutoIP), - StorageConnectivitySwitchless: pointer.From(input.StorageConnectivitySwitchless), - StorageNetwork: FlattenDeploymentSettingHostNetworkStorageNetwork(input.StorageNetworks), + Intent: FlattenDeploymentSettingHostNetworkIntent(input.Intents), + StorageAutoIpEnabled: pointer.From(input.EnableStorageAutoIP), + StorageConnectivitySwitchlessEnabled: pointer.From(input.StorageConnectivitySwitchless), + StorageNetwork: FlattenDeploymentSettingHostNetworkStorageNetwork(input.StorageNetworks), }} } @@ -931,14 +1009,14 @@ func ExpandDeploymentSettingHostNetworkIntent(input []HostNetworkIntentModel) *[ for _, item := range input { results = append(results, deploymentsettings.Intents{ Adapter: pointer.To(item.Adapter), - AdapterPropertyOverrides: ExpandHostNetworkIntentAdapterPropertyOverride(item.AdapterPropertyOverride), + AdapterPropertyOverrides: ExpandHostNetworkIntentAdapterPropertyOverride(item.OverrideAdapterProperty), Name: pointer.To(item.Name), OverrideAdapterProperty: pointer.To(item.OverrideAdapterPropertyEnabled), OverrideQosPolicy: pointer.To(item.OverrideQosPolicyEnabled), OverrideVirtualSwitchConfiguration: pointer.To(item.OverrideVirtualSwitchConfigurationEnabled), - QosPolicyOverrides: ExpandHostNetworkIntentQosPolicyOverride(item.QosPolicyOverride), + QosPolicyOverrides: ExpandHostNetworkIntentQosPolicyOverride(item.OverrideQosPolicy), TrafficType: pointer.To(item.TrafficType), - VirtualSwitchConfigurationOverrides: ExpandHostNetworkIntentVirtualSwitchConfigurationOverride(item.VirtualSwitchConfigurationOverride), + VirtualSwitchConfigurationOverrides: ExpandHostNetworkIntentVirtualSwitchConfigurationOverride(item.OverrideVirtualSwitchConfiguration), }) } @@ -954,21 +1032,21 @@ func FlattenDeploymentSettingHostNetworkIntent(input *[]deploymentsettings.Inten for _, item := range *input { results = append(results, HostNetworkIntentModel{ Adapter: pointer.From(item.Adapter), - AdapterPropertyOverride: FlattenHostNetworkIntentAdapterPropertyOverride(item.AdapterPropertyOverrides), + OverrideAdapterProperty: FlattenHostNetworkIntentAdapterPropertyOverride(item.AdapterPropertyOverrides), Name: pointer.From(item.Name), OverrideAdapterPropertyEnabled: pointer.From(item.OverrideAdapterProperty), OverrideQosPolicyEnabled: pointer.From(item.OverrideQosPolicy), OverrideVirtualSwitchConfigurationEnabled: pointer.From(item.OverrideVirtualSwitchConfiguration), - QosPolicyOverride: FlattenHostNetworkIntentQosPolicyOverride(item.QosPolicyOverrides), + OverrideQosPolicy: FlattenHostNetworkIntentQosPolicyOverride(item.QosPolicyOverrides), TrafficType: pointer.From(item.TrafficType), - VirtualSwitchConfigurationOverride: FlattenHostNetworkIntentVirtualSwitchConfigurationOverride(item.VirtualSwitchConfigurationOverrides), + OverrideVirtualSwitchConfiguration: FlattenHostNetworkIntentVirtualSwitchConfigurationOverride(item.VirtualSwitchConfigurationOverrides), }) } return results } -func ExpandHostNetworkIntentAdapterPropertyOverride(input []HostNetworkIntentAdapterPropertyOverrideModel) *deploymentsettings.AdapterPropertyOverrides { +func ExpandHostNetworkIntentAdapterPropertyOverride(input []OverrideHostNetworkIntentAdapterPropertyModel) *deploymentsettings.AdapterPropertyOverrides { if len(input) == 0 { return &deploymentsettings.AdapterPropertyOverrides{ JumboPacket: pointer.To(""), @@ -986,9 +1064,9 @@ func ExpandHostNetworkIntentAdapterPropertyOverride(input []HostNetworkIntentAda } } -func FlattenHostNetworkIntentAdapterPropertyOverride(input *deploymentsettings.AdapterPropertyOverrides) []HostNetworkIntentAdapterPropertyOverrideModel { +func FlattenHostNetworkIntentAdapterPropertyOverride(input *deploymentsettings.AdapterPropertyOverrides) []OverrideHostNetworkIntentAdapterPropertyModel { if input == nil { - return make([]HostNetworkIntentAdapterPropertyOverrideModel, 0) + return make([]OverrideHostNetworkIntentAdapterPropertyModel, 0) } jumboPacket := pointer.From(input.JumboPacket) @@ -997,17 +1075,17 @@ func FlattenHostNetworkIntentAdapterPropertyOverride(input *deploymentsettings.A // server will return the block with empty string in all fields by default if jumboPacket == "" && networkDirect == "" && networkDirectTechnology == "" { - return make([]HostNetworkIntentAdapterPropertyOverrideModel, 0) + return make([]OverrideHostNetworkIntentAdapterPropertyModel, 0) } - return []HostNetworkIntentAdapterPropertyOverrideModel{{ + return []OverrideHostNetworkIntentAdapterPropertyModel{{ JumboPacket: jumboPacket, NetworkDirect: networkDirect, NetworkDirectTechnology: networkDirectTechnology, }} } -func ExpandHostNetworkIntentQosPolicyOverride(input []HostNetworkIntentQosPolicyOverrideModel) *deploymentsettings.QosPolicyOverrides { +func ExpandHostNetworkIntentQosPolicyOverride(input []OverrideHostNetworkIntentQosPolicyModel) *deploymentsettings.QosPolicyOverrides { if len(input) == 0 { return &deploymentsettings.QosPolicyOverrides{ BandwidthPercentageSMB: pointer.To(""), @@ -1025,9 +1103,9 @@ func ExpandHostNetworkIntentQosPolicyOverride(input []HostNetworkIntentQosPolicy } } -func FlattenHostNetworkIntentQosPolicyOverride(input *deploymentsettings.QosPolicyOverrides) []HostNetworkIntentQosPolicyOverrideModel { +func FlattenHostNetworkIntentQosPolicyOverride(input *deploymentsettings.QosPolicyOverrides) []OverrideHostNetworkIntentQosPolicyModel { if input == nil { - return make([]HostNetworkIntentQosPolicyOverrideModel, 0) + return make([]OverrideHostNetworkIntentQosPolicyModel, 0) } bandwidthPercentageSMB := pointer.From(input.BandwidthPercentageSMB) @@ -1036,17 +1114,17 @@ func FlattenHostNetworkIntentQosPolicyOverride(input *deploymentsettings.QosPoli // server will return the block with empty string in all fields by default if bandwidthPercentageSMB == "" && priorityValue8021ActionCluster == "" && priorityValue8021ActionSMB == "" { - return make([]HostNetworkIntentQosPolicyOverrideModel, 0) + return make([]OverrideHostNetworkIntentQosPolicyModel, 0) } - return []HostNetworkIntentQosPolicyOverrideModel{{ + return []OverrideHostNetworkIntentQosPolicyModel{{ BandWidthPercentageSMB: bandwidthPercentageSMB, PriorityValue8021ActionCluster: priorityValue8021ActionCluster, PriorityValue8021ActionSMB: priorityValue8021ActionSMB, }} } -func ExpandHostNetworkIntentVirtualSwitchConfigurationOverride(input []HostNetworkIntentVirtualSwitchConfigurationOverrideModel) *deploymentsettings.VirtualSwitchConfigurationOverrides { +func ExpandHostNetworkIntentVirtualSwitchConfigurationOverride(input []OverrideHostNetworkIntentVirtualSwitchConfigurationModel) *deploymentsettings.VirtualSwitchConfigurationOverrides { if len(input) == 0 { return &deploymentsettings.VirtualSwitchConfigurationOverrides{ EnableIov: pointer.To(""), @@ -1062,9 +1140,9 @@ func ExpandHostNetworkIntentVirtualSwitchConfigurationOverride(input []HostNetwo } } -func FlattenHostNetworkIntentVirtualSwitchConfigurationOverride(input *deploymentsettings.VirtualSwitchConfigurationOverrides) []HostNetworkIntentVirtualSwitchConfigurationOverrideModel { +func FlattenHostNetworkIntentVirtualSwitchConfigurationOverride(input *deploymentsettings.VirtualSwitchConfigurationOverrides) []OverrideHostNetworkIntentVirtualSwitchConfigurationModel { if input == nil { - return make([]HostNetworkIntentVirtualSwitchConfigurationOverrideModel, 0) + return make([]OverrideHostNetworkIntentVirtualSwitchConfigurationModel, 0) } enableIov := pointer.From(input.EnableIov) @@ -1072,10 +1150,10 @@ func FlattenHostNetworkIntentVirtualSwitchConfigurationOverride(input *deploymen // server will return the block with empty string in all fields by default if enableIov == "" && loadBalancingAlgorithm == "" { - return make([]HostNetworkIntentVirtualSwitchConfigurationOverrideModel, 0) + return make([]OverrideHostNetworkIntentVirtualSwitchConfigurationModel, 0) } - return []HostNetworkIntentVirtualSwitchConfigurationOverrideModel{{ + return []OverrideHostNetworkIntentVirtualSwitchConfigurationModel{{ EnableIov: enableIov, LoadBalancingAlgorithm: loadBalancingAlgorithm, }} @@ -1185,32 +1263,14 @@ func FlattenDeploymentSettingInfrastructureNetworkIpPool(input *[]deploymentsett return results } -func ExpandDeploymentSettingObservability(input []ObservabilityModel) *deploymentsettings.Observability { - if len(input) == 0 { - return nil - } - - v := input[0] - +func ExpandDeploymentSettingObservability(input ScaleUnitModel) *deploymentsettings.Observability { return &deploymentsettings.Observability{ - EpisodicDataUpload: pointer.To(v.EpisodicDataUploadEnabled), - EuLocation: pointer.To(v.EuLocationEnabled), - StreamingDataClient: pointer.To(v.StreamingDataClientEnabled), + EpisodicDataUpload: pointer.To(input.EpisodicDataUploadEnabled), + EuLocation: pointer.To(input.EuLocationEnabled), + StreamingDataClient: pointer.To(input.StreamingDataClientEnabled), } } -func FlattenDeploymentSettingObservability(input *deploymentsettings.Observability) []ObservabilityModel { - if input == nil { - return make([]ObservabilityModel, 0) - } - - return []ObservabilityModel{{ - EpisodicDataUploadEnabled: pointer.From(input.EpisodicDataUpload), - EuLocationEnabled: pointer.From(input.EuLocation), - StreamingDataClientEnabled: pointer.From(input.StreamingDataClient), - }} -} - func ExpandDeploymentSettingOptionalService(input []OptionalServiceModel) *deploymentsettings.OptionalServices { if len(input) == 0 { return nil @@ -1265,46 +1325,21 @@ func FlattenDeploymentSettingPhysicalNode(input *[]deploymentsettings.PhysicalNo return results } -func ExpandDeploymentSettingSecuritySetting(input []SecuritySettingModel) *deploymentsettings.DeploymentSecuritySettings { - if len(input) == 0 { - return nil - } - - v := input[0] - +func ExpandDeploymentSettingSecuritySetting(input ScaleUnitModel) *deploymentsettings.DeploymentSecuritySettings { return &deploymentsettings.DeploymentSecuritySettings{ - BitlockerBootVolume: pointer.To(v.BitlockerBootVolumeEnabled), - BitlockerDataVolumes: pointer.To(v.BitlockerDataVolumeEnabled), - CredentialGuardEnforced: pointer.To(v.CredentialGuardEnabled), - DriftControlEnforced: pointer.To(v.DriftControlEnabled), - DrtmProtection: pointer.To(v.DrtmProtectionEnabled), - HvciProtection: pointer.To(v.HvciProtectionEnabled), - SideChannelMitigationEnforced: pointer.To(v.SideChannelMitigationEnabled), - SmbClusterEncryption: pointer.To(v.SmbClusterEncryptionEnabled), - SmbSigningEnforced: pointer.To(v.SmbSigningEnabled), - WdacEnforced: pointer.To(v.WdacEnabled), + BitlockerBootVolume: pointer.To(input.BitlockerBootVolumeEnabled), + BitlockerDataVolumes: pointer.To(input.BitlockerDataVolumeEnabled), + CredentialGuardEnforced: pointer.To(input.CredentialGuardEnabled), + DriftControlEnforced: pointer.To(input.DriftControlEnabled), + DrtmProtection: pointer.To(input.DrtmProtectionEnabled), + HvciProtection: pointer.To(input.HvciProtectionEnabled), + SideChannelMitigationEnforced: pointer.To(input.SideChannelMitigationEnabled), + SmbClusterEncryption: pointer.To(input.SmbClusterEncryptionEnabled), + SmbSigningEnforced: pointer.To(input.SmbSigningEnabled), + WdacEnforced: pointer.To(input.WdacEnabled), } } -func FlattenDeploymentSettingSecuritySetting(input *deploymentsettings.DeploymentSecuritySettings) []SecuritySettingModel { - if input == nil { - return make([]SecuritySettingModel, 0) - } - - return []SecuritySettingModel{{ - BitlockerBootVolumeEnabled: pointer.From(input.BitlockerBootVolume), - BitlockerDataVolumeEnabled: pointer.From(input.BitlockerDataVolumes), - CredentialGuardEnabled: pointer.From(input.CredentialGuardEnforced), - DriftControlEnabled: pointer.From(input.DriftControlEnforced), - DrtmProtectionEnabled: pointer.From(input.DrtmProtection), - HvciProtectionEnabled: pointer.From(input.HvciProtection), - SideChannelMitigationEnabled: pointer.From(input.SideChannelMitigationEnforced), - SmbClusterEncryptionEnabled: pointer.From(input.SmbClusterEncryption), - SmbSigningEnabled: pointer.From(input.SmbSigningEnforced), - WdacEnabled: pointer.From(input.WdacEnforced), - }} -} - func ExpandDeploymentSettingStorage(input []StorageModel) *deploymentsettings.Storage { if len(input) == 0 { return nil diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go index e8979cd98dcd..b72de12e03cd 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go @@ -111,7 +111,12 @@ func (r StackHCIDeploymentSettingResource) basic(data acceptance.TestData) strin %s provider "azurerm" { - features {} + features { + azure_stack_hci { + delete_arc_bridge_on_destroy = true + delete_custom_location_on_destroy = true + } + } } resource "azurerm_stack_hci_deployment_setting" "test" { @@ -120,10 +125,25 @@ resource "azurerm_stack_hci_deployment_setting" "test" { version = "10.0.0.0" scale_unit { - adou_path = "OU=hci${var.random_string},DC=jumpstart,DC=local" - domain_fqdn = "jumpstart.local" - secrets_location = azurerm_key_vault.DeploymentKeyVault.vault_uri - naming_prefix = "hci${var.random_string}" + adou_path = "OU=hci${var.random_string},DC=jumpstart,DC=local" + domain_fqdn = "jumpstart.local" + secrets_location = azurerm_key_vault.DeploymentKeyVault.vault_uri + naming_prefix = "hci${var.random_string}" + streaming_data_client_enabled = true + eu_location_enabled = false + episodic_data_upload_enabled = true + + bitlocker_boot_volume_enabled = true + bitlocker_data_volume_enabled = true + credential_guard_enabled = true + drift_control_enabled = true + drtm_protection_enabled = true + hvci_protection_enabled = true + side_channel_mitigation_enabled = true + smb_cluster_encryption_enabled = false + smb_signing_enabled = true + wdac_enabled = true + cluster { azure_service_endpoint = "core.windows.net" @@ -196,24 +216,6 @@ resource "azurerm_stack_hci_deployment_setting" "test" { name = "AzSHOST2" } - observability { - streaming_data_client_enabled = true - eu_location_enabled = false - episodic_data_upload_enabled = true - } - - security_setting { - bitlocker_boot_volume_enabled = true - bitlocker_data_volume_enabled = true - credential_guard_enabled = true - drift_control_enabled = true - drtm_protection_enabled = true - hvci_protection_enabled = true - side_channel_mitigation_enabled = true - smb_cluster_encryption_enabled = false - smb_signing_enabled = true - wdac_enabled = true - } storage { configuration_mode = "Express" } @@ -229,7 +231,12 @@ func (r StackHCIDeploymentSettingResource) requiresImport(data acceptance.TestDa %s provider "azurerm" { - features {} + features { + azure_stack_hci { + delete_arc_bridge_on_destroy = true + delete_custom_location_on_destroy = true + } + } } resource "azurerm_stack_hci_deployment_setting" "import" { @@ -247,7 +254,12 @@ func (r StackHCIDeploymentSettingResource) complete(data acceptance.TestData) st %s provider "azurerm" { - features {} + features { + azure_stack_hci { + delete_arc_bridge_on_destroy = true + delete_custom_location_on_destroy = true + } + } } resource "azurerm_stack_hci_deployment_setting" "test" { @@ -256,10 +268,24 @@ resource "azurerm_stack_hci_deployment_setting" "test" { version = "10.0.0.0" scale_unit { - adou_path = "OU=hci${var.random_string},DC=jumpstart,DC=local" - domain_fqdn = "jumpstart.local" - secrets_location = azurerm_key_vault.DeploymentKeyVault.vault_uri - naming_prefix = "hci${var.random_string}" + adou_path = "OU=hci${var.random_string},DC=jumpstart,DC=local" + domain_fqdn = "jumpstart.local" + secrets_location = azurerm_key_vault.DeploymentKeyVault.vault_uri + naming_prefix = "hci${var.random_string}" + streaming_data_client_enabled = true + eu_location_enabled = false + episodic_data_upload_enabled = true + bitlocker_boot_volume_enabled = true + bitlocker_data_volume_enabled = true + credential_guard_enabled = true + drift_control_enabled = true + drtm_protection_enabled = true + hvci_protection_enabled = true + side_channel_mitigation_enabled = true + smb_cluster_encryption_enabled = false + smb_signing_enabled = true + wdac_enabled = true + cluster { azure_service_endpoint = "core.windows.net" @@ -270,8 +296,9 @@ resource "azurerm_stack_hci_deployment_setting" "test" { } host_network { - storage_auto_ip_enabled = true - storage_connectivity_switchless = false + storage_auto_ip_enabled = true + storage_connectivity_switchless_enabled = false + intent { name = "ManagementCompute" override_adapter_property_enabled = false @@ -285,12 +312,12 @@ resource "azurerm_stack_hci_deployment_setting" "test" { "Management", "Compute", ] - qos_policy_override { + override_qos_policy { priority_value8021_action_cluster = "7" priority_value8021_action_smb = "3" bandwidth_percentage_smb = "50" } - adapter_property_override { + override_adapter_property { jumbo_packet = "9014" network_direct = "Disabled" network_direct_technology = "RoCEv2" @@ -309,12 +336,12 @@ resource "azurerm_stack_hci_deployment_setting" "test" { traffic_type = [ "Storage", ] - qos_policy_override { + override_qos_policy { priority_value8021_action_cluster = "7" priority_value8021_action_smb = "3" bandwidth_percentage_smb = "50" } - adapter_property_override { + override_adapter_property { jumbo_packet = "9014" network_direct = "Enabled" network_direct_technology = "RoCEv2" @@ -361,25 +388,6 @@ resource "azurerm_stack_hci_deployment_setting" "test" { name = "AzSHOST2" } - observability { - streaming_data_client_enabled = true - eu_location_enabled = false - episodic_data_upload_enabled = true - } - - security_setting { - bitlocker_boot_volume_enabled = true - bitlocker_data_volume_enabled = true - credential_guard_enabled = true - drift_control_enabled = true - drtm_protection_enabled = true - hvci_protection_enabled = true - side_channel_mitigation_enabled = true - smb_cluster_encryption_enabled = false - smb_signing_enabled = true - wdac_enabled = true - } - storage { configuration_mode = "Express" } @@ -548,7 +556,7 @@ resource "azuread_application" "test" { } resource "azuread_service_principal" "test" { - application_id = azuread_application.test.application_id + client_id = azuread_application.test.client_id } resource "azuread_service_principal_password" "test" { @@ -562,6 +570,12 @@ resource "azurerm_role_assignment" "connect" { principal_id = azuread_service_principal.test.object_id } +resource "azurerm_role_assignment" "connect2" { + scope = data.azurerm_subscription.current.id + role_definition_name = "Contributor" + principal_id = azuread_service_principal.test.object_id +} + // this is following https://learn.microsoft.com/en-us/azure-stack/hci/deploy/deployment-tool-active-directory resource "terraform_data" "ad_creation_provisioner" { depends_on = [azurerm_virtual_machine.test] @@ -582,6 +596,7 @@ resource "terraform_data" "provisioner" { depends_on = [ terraform_data.ad_creation_provisioner, azurerm_role_assignment.connect, + azurerm_role_assignment.connect2, ] provisioner "local-exec" { @@ -589,7 +604,7 @@ resource "terraform_data" "provisioner" { } provisioner "local-exec" { - command = "powershell.exe -ExecutionPolicy Bypass -NoProfile -File ./testdata/connect.ps1 -userName ${var.local_admin_user} -password \"${var.local_admin_password}\" -authType Credssp -ip ${azurerm_public_ip.test.ip_address} -port ${local.servers[count.index].port} -subscriptionId ${data.azurerm_client_config.current.subscription_id} -resourceGroupName ${azurerm_resource_group.test2.name} -region ${azurerm_resource_group.test2.location} -tenant ${data.azurerm_client_config.current.tenant_id} -servicePrincipalId ${azuread_service_principal.test.object_id} -servicePrincipalSecret ${azuread_service_principal_password.test.value} -expandC true" + command = "powershell.exe -ExecutionPolicy Bypass -NoProfile -File ./testdata/connect.ps1 -userName ${var.local_admin_user} -password \"${var.local_admin_password}\" -authType Credssp -ip ${azurerm_public_ip.test.ip_address} -port ${local.servers[count.index].port} -subscriptionId ${data.azurerm_client_config.current.subscription_id} -resourceGroupName ${azurerm_resource_group.test2.name} -region ${azurerm_resource_group.test2.location} -tenant ${data.azurerm_client_config.current.tenant_id} -servicePrincipalId ${azuread_service_principal.test.client_id} -servicePrincipalSecret ${azuread_service_principal_password.test.value} -expandC true" interpreter = ["PowerShell", "-Command"] } @@ -650,7 +665,7 @@ resource "azurerm_key_vault_secret" "LocalAdminCredential" { resource "azurerm_key_vault_secret" "DefaultARBApplication" { name = "DefaultARBApplication" content_type = "Secret" - value = base64encode("${azuread_service_principal.test.object_id}:${azuread_service_principal_password.test.value}") + value = base64encode("${azuread_service_principal.test.client_id}:${azuread_service_principal_password.test.value}") key_vault_id = azurerm_key_vault.DeploymentKeyVault.id depends_on = [azurerm_role_assignment.KeyVault] } diff --git a/internal/services/azurestackhci/testdata/ad.ps1 b/internal/services/azurestackhci/testdata/ad.ps1 index 5866cc49b1fc..5bf9d14048a5 100644 --- a/internal/services/azurestackhci/testdata/ad.ps1 +++ b/internal/services/azurestackhci/testdata/ad.ps1 @@ -1,3 +1,5 @@ +# Follows https://learn.microsoft.com/en-us/azure-stack/hci/deploy/deployment-prep-active-directory +# The script is from https://github.com/Azure/Edge-infrastructure-quickstart-template/blob/c42d671bd8464071c3b8dfc9d599bef1631b22a2/modules/hci-provisioners/ad.ps1 param( $userName, $password, diff --git a/internal/services/azurestackhci/testdata/connect.ps1 b/internal/services/azurestackhci/testdata/connect.ps1 index bc5f4c438249..e67d595be68a 100644 --- a/internal/services/azurestackhci/testdata/connect.ps1 +++ b/internal/services/azurestackhci/testdata/connect.ps1 @@ -1,3 +1,5 @@ +# follows https://learn.microsoft.com/en-us/azure-stack/hci/deploy/deployment-arc-register-server-permissions?tabs=powershell +# the script is from https://github.com/Azure/Edge-infrastructure-quickstart-template/blob/c42d671bd8464071c3b8dfc9d599bef1631b22a2/modules/hci-provisioners/hci-server/connect.ps1 param( $userName, $password, diff --git a/internal/services/extendedlocation/client/client.go b/internal/services/extendedlocation/client/client.go new file mode 100644 index 000000000000..74b0d429e159 --- /dev/null +++ b/internal/services/extendedlocation/client/client.go @@ -0,0 +1,26 @@ +package client + +import ( + "fmt" + + extendedLocation20210815 "github.com/hashicorp/go-azure-sdk/resource-manager/extendedlocation/2021-08-15" + "github.com/hashicorp/go-azure-sdk/sdk/client/resourcemanager" + "github.com/hashicorp/terraform-provider-azurerm/internal/common" +) + +type Client struct { + *extendedLocation20210815.Client +} + +func NewClient(o *common.ClientOptions) (*Client, error) { + client, err := extendedLocation20210815.NewClientWithBaseURI(o.Environment.ResourceManager, func(c *resourcemanager.Client) { + o.Configure(c, o.Authorizers.ResourceManager) + }) + if err != nil { + return nil, fmt.Errorf("building clients for Network: %+v", err) + } + + return &Client{ + Client: client, + }, nil +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/extendedlocation/2021-08-15/client.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/extendedlocation/2021-08-15/client.go new file mode 100644 index 000000000000..07876ce117f8 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/extendedlocation/2021-08-15/client.go @@ -0,0 +1,28 @@ +package v2021_08_15 + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +import ( + "fmt" + + "github.com/hashicorp/go-azure-sdk/resource-manager/extendedlocation/2021-08-15/customlocations" + "github.com/hashicorp/go-azure-sdk/sdk/client/resourcemanager" + sdkEnv "github.com/hashicorp/go-azure-sdk/sdk/environments" +) + +type Client struct { + CustomLocations *customlocations.CustomLocationsClient +} + +func NewClientWithBaseURI(sdkApi sdkEnv.Api, configureFunc func(c *resourcemanager.Client)) (*Client, error) { + customLocationsClient, err := customlocations.NewCustomLocationsClientWithBaseURI(sdkApi) + if err != nil { + return nil, fmt.Errorf("building CustomLocations client: %+v", err) + } + configureFunc(customLocationsClient.Client) + + return &Client{ + CustomLocations: customLocationsClient, + }, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 498fc8f9791b..3fb07a600b96 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -519,6 +519,7 @@ github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/namespace github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/networkrulesets github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2021-11-01/schemaregistry github.com/hashicorp/go-azure-sdk/resource-manager/eventhub/2022-01-01-preview/namespaces +github.com/hashicorp/go-azure-sdk/resource-manager/extendedlocation/2021-08-15 github.com/hashicorp/go-azure-sdk/resource-manager/extendedlocation/2021-08-15/customlocations github.com/hashicorp/go-azure-sdk/resource-manager/fluidrelay/2022-05-26 github.com/hashicorp/go-azure-sdk/resource-manager/fluidrelay/2022-05-26/fluidrelaycontainers diff --git a/website/docs/guides/features-block.html.markdown b/website/docs/guides/features-block.html.markdown index 2e2ddae26ad9..5b7e32130682 100644 --- a/website/docs/guides/features-block.html.markdown +++ b/website/docs/guides/features-block.html.markdown @@ -41,6 +41,11 @@ provider "azurerm" { disable_generated_rule = false } + azure_stack_hci { + delete_arc_bridge_on_destroy = false + delete_custom_location_on_destroy = false + } + cognitive_account { purge_soft_delete_on_destroy = true } @@ -108,6 +113,8 @@ The `features` block supports the following: * `application_insights` - (Optional) An `application_insights` block as defined below. +* `azure_stack_hci` - (Optional) An `azure_stack_hci` block as defined below. + * `cognitive_account` - (Optional) A `cognitive_account` block as defined below. * `key_vault` - (Optional) A `key_vault` block as defined below. @@ -152,6 +159,14 @@ The `application_insights` block supports the following: --- +The `azure_stack_hci` block supports the following: + +* `delete_arc_bridge_on_destroy` - (Optional) Should the `azurerm_stack_hci_deployment_setting` resources delete the Arc Resource Bridge during the destroy step? Defaults to `false`. + +* `delete_custom_location_on_destroy` - (Optional) Should the `azurerm_stack_hci_deployment_setting` resources delete the Custom Location and the Azure Stack HCI Storage Containers in the location during the destroy step? Defaults to `false`. + +--- + The `cognitive_account` block supports the following: * `purge_soft_delete_on_destroy` - (Optional) Should the `azurerm_cognitive_account` resources be permanently deleted (e.g. purged) when destroyed? Defaults to `true`. diff --git a/website/docs/r/stack_hci_deployment_setting.html.markdown b/website/docs/r/stack_hci_deployment_setting.html.markdown index 783c6ac38793..69e75789d00d 100644 --- a/website/docs/r/stack_hci_deployment_setting.html.markdown +++ b/website/docs/r/stack_hci_deployment_setting.html.markdown @@ -10,13 +10,23 @@ description: |- Manages a Stack HCI Deployment Setting. +-> Note: Completion of the prerequisites of deploying the Azure Stack HCI in your environment is outside the scope of this document. For more details refer to the [Azure Stack HCI deployment sequence](https://learn.microsoft.com/en-us/azure-stack/hci/deploy/deployment-introduction#deployment-sequence). If you encounter issues completing the prerequisites, we'd recommend opening a ticket with Microsoft Support. + +-> Note: The Azure Provider include a Feature Toggle `delete_arc_bridge_on_destroy` which controls whether to delete Arc Resource Bridge generated on destroy, and Feature Toggle `delete_custom_location_on_destroy` which controls whether to delete Custom Location on destroy. The Provider will not do the deletion by default. See [the Features block documentation](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/features-block) for more information on Feature Toggles within Terraform. + + ## Example Usage ```hcl provider "azuread" {} provider "azurerm" { - features {} + features { + azure_stack_hci { + delete_arc_bridge_on_destroy = false + delete_custom_location_on_destroy = false + } + } } variable "local_admin_user" { @@ -103,7 +113,13 @@ resource "azurerm_role_assignment" "connect" { principal_id = azuread_service_principal.example.object_id } -## prepare Active Directory and register with Arc, and then the Arc VM is ready +resource "azurerm_role_assignment" "connect" { + scope = data.azurerm_subscription.current.id + role_definition_name = "Contributor" + principal_id = azuread_service_principal.example.object_id +} + +# after preparing Active Directory and registering servers with Arc, and then the Arc VM is ready data "azurerm_arc_machine" "server" { count = length(local.servers) name = local.servers[count.index].name @@ -151,7 +167,7 @@ resource "azurerm_key_vault_secret" "LocalAdminCredential" { resource "azurerm_key_vault_secret" "DefaultARBApplication" { name = "DefaultARBApplication" content_type = "Secret" - value = base64encode("${azuread_service_principal.example.object_id}:${azuread_service_principal_password.example.value}") + value = base64encode("${azuread_service_principal.example.client_id}:${azuread_service_principal_password.example.value}") key_vault_id = azurerm_key_vault.DeploymentKeyVault.id depends_on = [azurerm_role_assignment.KeyVault] } @@ -204,6 +220,7 @@ resource "azurerm_stack_hci_cluster" "example" { azurerm_key_vault_secret.LocalAdminCredential, azurerm_key_vault_secret.WitnessStorageKey, azurerm_role_assignment.connect, + azurerm_role_assignment.connect2, azurerm_role_assignment.ServicePrincipalRoleAssign, azurerm_role_assignment.MachineRoleAssign1, azurerm_role_assignment.MachineRoleAssign2, @@ -243,8 +260,8 @@ resource "azurerm_stack_hci_deployment_setting" "example" { } host_network { - storage_auto_ip_enabled = true - storage_connectivity_switchless = false + storage_auto_ip_enabled = true + storage_connectivity_switchless_enabled = false intent { name = "ManagementCompute" override_adapter_property_enabled = false @@ -258,12 +275,12 @@ resource "azurerm_stack_hci_deployment_setting" "example" { "Management", "Compute", ] - qos_policy_override { + override_qos_policy { priority_value8021_action_cluster = "7" priority_value8021_action_smb = "3" bandwidth_percentage_smb = "50" } - adapter_property_override { + override_adapter_property { jumbo_packet = "9014" network_direct = "Disabled" network_direct_technology = "RoCEv2" @@ -282,12 +299,12 @@ resource "azurerm_stack_hci_deployment_setting" "example" { traffic_type = [ "Storage", ] - qos_policy_override { + override_qos_policy { priority_value8021_action_cluster = "7" priority_value8021_action_smb = "3" bandwidth_percentage_smb = "50" } - adapter_property_override { + override_adapter_property { jumbo_packet = "9014" network_direct = "Enabled" network_direct_technology = "RoCEv2" @@ -370,31 +387,21 @@ The following arguments are supported: * `scale_unit` - (Required) One or more `scale_unit` blocks as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. -* `version` - (Required) The deployment template version, possible value is `10.0.0.0`. Changing this forces a new Stack HCI Deployment Setting to be created. - ---- - -A `adapter_property_override` block supports the following: - -* `jumbo_packet` - (Optional) The jumbo frame size of the adapter. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. - -* `network_direct` - (Optional) The network direct of the adapter. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. - -* `network_direct_technology` - (Optional) The network direct technology of the adapter. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. +* `version` - (Required) The deployment template version. The format must be a set of numbers separated by dots such as `10.0.0.0`. Changing this forces a new Stack HCI Deployment Setting to be created. --- A `cluster` block supports the following: -* `azure_service_endpoint` - (Required) For Azure blob service endpoint type, select either Default or Custom domain. If you selected Custom domain, enter the domain for the blob service in this format `core.windows.net`. Changing this forces a new Stack HCI Deployment Setting to be created. +* `azure_service_endpoint` - (Required) Specifies the Azure blob service endpoint, for example, `core.windows.net`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `cloud_account_name` - (Required) Specify the Azure Storage account name for cloud witness for your Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. +* `cloud_account_name` - (Required) Specifies the Azure Storage account name of the cloud witness for the Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. -* `name` - (Required) The cluster name provided when preparing Active Directory. It must be 3-15 characters long and contain only letters, numbers and hyphens. Changing this forces a new Stack HCI Deployment Setting to be created. +* `name` - (Required) Specifies the name of the cluster. It must be 3-15 characters long and contain only letters, numbers and hyphens. Changing this forces a new Stack HCI Deployment Setting to be created. -* `witness_path` - (Required) Specify the fileshare path for the local witness for your Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. +* `witness_path` - (Required) Specifies the fileshare path of the local witness for the Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. -* `witness_type` - (Required) Use a cloud witness if you have internet access and if you use an Azure Storage account to provide a vote on cluster quorum. A cloud witness uses Azure Blob Storage to read or write a blob file and then uses it to arbitrate in split-brain resolution. Possible values are `Cloud`, `FileShare`. Changing this forces a new Stack HCI Deployment Setting to be created. +* `witness_type` - (Required) Specifies the type of the witness. Possible values are `Cloud`, `FileShare`. Changing this forces a new Stack HCI Deployment Setting to be created. --- @@ -404,9 +411,9 @@ A `host_network` block supports the following: * `storage_network` - (Required) One or more `storage_network` blocks as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. -* `storage_auto_ip_enabled` - (Optional) Whether allows users to specify IPs and Mask for Storage NICs when Network ATC is not assigning the IPs for storage automatically. Optional parameter required only for 3 Nodes Switchless deployments. Possible values are `true` and `false`. Defaults to `true`. Changing this forces a new Stack HCI Deployment Setting to be created. +* `storage_auto_ip_enabled` - (Optional) Whether allows users to specify IPs and Mask for Storage NICs when Network ATC is not assigning the IPs for storage automatically. Optional parameter required only for [3 Nodes Switchless deployments](https://learn.microsoft.com/azure-stack/hci/concepts/physical-network-requirements?tabs=overview%2C23H2reqs#using-switchless). Possible values are `true` and `false`. Defaults to `true`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `storage_connectivity_switchless` - (Optional) Defines how the storage adapters between nodes are connected either switch or switch less. Possible values are `true` and `false`. Defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. +* `storage_connectivity_switchless_enabled` - (Optional) Defines how the storage adapters between nodes are connected either switch or switch less. Possible values are `true` and `false`. Defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. --- @@ -420,7 +427,9 @@ A `infrastructure_network` block supports the following: * `subnet_mask` - (Required) Specifies the subnet mask that matches the provided IP address space. Changing this forces a new Stack HCI Deployment Setting to be created. -* `dhcp_enabled` - (Optional) Whether allows customers to use DHCP for Hosts and Cluster IPs. If disabled, the deployment will Defaults to static IPs. If enabled, gateway and DNS servers are not required. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. +* `dhcp_enabled` - (Optional) Whether DHCP is enabled for hosts and cluster IPs. Possible values are `true` and `false`. defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. + +-> **NOTE:** If `dhcp_enabled` is set to `false`, the deployment will use static IPs. If set to `true`, the gateway and DNS servers are not required. --- @@ -432,17 +441,17 @@ A `intent` block supports the following: * `traffic_type` - (Required) Specifies a list of network traffic types. Possible values are `Compute`, `Storage`, `Management`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `adapter_property_override` - (Optional) A `adapter_property_override` block as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. +* `override_adapter_property` - (Optional) A `override_adapter_property` block as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. -* `qos_policy_override` - (Optional) A `qos_policy_override` block as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. +* `override_adapter_property_enabled` - (Optional) Whether to override adapter properties. Possible values are `true` and `false`. defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `virtual_switch_configuration_override` - (Optional) A `virtual_switch_configuration_override` block as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. +* `override_qos_policy` - (Optional) A `override_qos_policy` block as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. -* `override_adapter_property_enabled` - (Optional) This parameter should only be modified based on your OEM guidance. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. +* `override_qos_policy_enabled` - (Optional) Whether to override QoS policy. Possible values are `true` and `false`. defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `override_qos_policy_enabled` - (Optional) This parameter should only be modified based on your OEM guidance. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. +* `override_virtual_switch_configuration` - (Optional) A `override_virtual_switch_configuration` block as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. -* `override_virtual_switch_configuration_enabled` - (Optional) This parameter should only be modified based on your OEM guidance. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. +* `override_virtual_switch_configuration_enabled` - (Optional) Whether to override virtual switch configuration. Possible values are `true` and `false`. defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. --- @@ -460,7 +469,7 @@ A `observability` block supports the following: * `eu_location_enabled` - (Required) Whether to store data sent to Microsoft in EU. The log and diagnostic data is sent to the appropriate diagnostics servers depending upon where your cluster resides. Setting this to `false` results in all data sent to Microsoft to be stored outside of the EU. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `streaming_data_client_enabled` - (Required) Whether send telemetry data to Microsoft. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. +* `streaming_data_client_enabled` - (Required) Whether the telemetry data will be sent to Microsoft. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. --- @@ -470,17 +479,19 @@ A `optional_service` block supports the following: --- -A `physical_node` block supports the following: +A `override_adapter_property` block supports the following: -* `ipv4_address` - (Required) Specifies the IPv4 address assigned to each physical server on your Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. +* `jumbo_packet` - (Optional) The jumbo frame size of the adapter. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. -* `name` - (Required) The NETBIOS name of each physical server on your Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. +* `network_direct` - (Optional) The network direct of the adapter. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `network_direct_technology` - (Optional) The network direct technology of the adapter. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. --- -A `qos_policy_override` block supports the following: +A `override_qos_policy` block supports the following: -* `bandwidth_percentage_smb` - (Optional) Specifies the bandwidth allocation in % for the storage traffic. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. +* `bandwidth_percentage_smb` - (Optional) Specifies the percentage of the allocated storage traffic bandwidth. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. * `priority_value8021_action_cluster` - (Optional) Specifies the Cluster traffic priority. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. @@ -488,13 +499,29 @@ A `qos_policy_override` block supports the following: --- +A `override_virtual_switch_configuration` block supports the following: + +* `enable_iov` - (Optional) Specifies the IoV enable status for Virtual Switch. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `load_balancing_algorithm` - (Optional) Specifies the load balancing algorithm for Virtual Switch. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + +A `physical_node` block supports the following: + +* `ipv4_address` - (Required) Specifies the IPv4 address assigned to each physical server on your Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `name` - (Required) The NETBIOS name of each physical server on your Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + A `scale_unit` block supports the following: * `adou_path` - (Required) Specify the full name of the Active Directory Organizational Unit container object prepared for the deployment, including the domain components. For example:`OU=HCI01,DC=contoso,DC=com`. Changing this forces a new Stack HCI Deployment Setting to be created. * `cluster` - (Required) A `cluster` block as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. -* `domain_fqdn` - (Required) Specifies the FQDN to deploy cluster. Changing this forces a new Stack HCI Deployment Setting to be created. +* `domain_fqdn` - (Required) Specifies the FQDN for deploying cluster. Changing this forces a new Stack HCI Deployment Setting to be created. * `host_network` - (Required) A `host_network` block as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. @@ -506,37 +533,37 @@ A `scale_unit` block supports the following: * `physical_node` - (Required) One or more `physical_node` blocks as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. -* `secrets_location` - (Required) The URI to the keyvault or secret store. Changing this forces a new Stack HCI Deployment Setting to be created. +* `secrets_location` - (Required) The URI to the Key Vault or secret store. Changing this forces a new Stack HCI Deployment Setting to be created. * `security_setting` - (Required) A `security_setting` block as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. * `storage` - (Required) A `storage` block as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. -* `observability` - (Required) A `observability` block as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. +* `episodic_data_upload_enabled` - (Optional) Whether to collect log data to facilitate quicker issue resolution. Possible values are `true` and `false`. Defaults to `true`. Changing this forces a new Stack HCI Deployment Setting to be created. ---- +* `eu_location_enabled` - (Optional) Whether to store data sent to Microsoft in EU. The log and diagnostic data is sent to the appropriate diagnostics servers depending upon where your cluster resides. Setting this to `false` results in all data sent to Microsoft to be stored outside of the EU. Possible values are `true` and `false`. Defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. -A `security_setting` block supports the following: +* `streaming_data_client_enabled` - (Optional) Whether the telemetry data will be sent to Microsoft. Possible values are `true` and `false`. Defaults to `true`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `bitlocker_boot_volume_enabled` - (Required) Whether to enable BitLocker for boot volume. When set to `true`, BitLocker XTS_AES 256-bit encryption is enabled for all data-at-rest on the OS volume of your Azure Stack HCI cluster. This setting is TPM-hardware dependent. Changing this forces a new Stack HCI Deployment Setting to be created. +* `bitlocker_boot_volume_enabled` - (Optional) Whether to enable BitLocker for boot volume. Possible values are `true` and `false`. When set to `true`, BitLocker XTS_AES 256-bit encryption is enabled for all data-at-rest on the OS volume of your Azure Stack HCI cluster. This setting is TPM-hardware dependent. Defaults to `true`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `bitlocker_data_volume_enabled` - (Required) Whether to enable BitLocker for data volume. When set to `true`, BitLocker XTS-AES 256-bit encryption is enabled for all data-at-rest on your Azure Stack HCI cluster shared volumes. Changing this forces a new Stack HCI Deployment Setting to be created. +* `bitlocker_data_volume_enabled` - (Optional) Whether to enable BitLocker for data volume. Possible values are `true` and `false`. When set to `true`, BitLocker XTS-AES 256-bit encryption is enabled for all data-at-rest on your Azure Stack HCI cluster shared volumes. Defaults to `true`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `credential_guard_enabled` - (Required) Whether to enable credential guard. Changing this forces a new Stack HCI Deployment Setting to be created. +* `credential_guard_enabled` - (Optional) Whether to enable credential guard. Possible values are `true` and `false`. Defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `drift_control_enabled` - (Required) Whether to enable drift control. When set to `true`, the security baseline is re-applied regularly. Changing this forces a new Stack HCI Deployment Setting to be created. +* `drift_control_enabled` - (Optional) Whether to enable drift control. Possible values are `true` and `false`. When set to `true`, the security baseline is re-applied regularly. Defaults to `true`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `drtm_protection_enabled` - (Required) Whether to enable DRTM protection. When set to `true`, Secure Boot is enabled on your Azure HCI cluster. This setting is hardware dependent. Changing this forces a new Stack HCI Deployment Setting to be created. +* `drtm_protection_enabled` - (Optional) Whether to enable DRTM protection. Possible values are `true` and `false`. When set to `true`, Secure Boot is enabled on your Azure HCI cluster. This setting is hardware dependent. Defaults to `true`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `hvci_protection_enabled` - (Required) Whether to enable HVCI protection. When set to `true`, Hypervisor-protected Code Integrity is enabled on your Azure HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. +* `hvci_protection_enabled` - (Optional) Whether to enable HVCI protection. Possible values are `true` and `false`. When set to `true`, Hypervisor-protected Code Integrity is enabled on your Azure HCI cluster. Defaults to `true`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `side_channel_mitigation_enabled` - (Required) Whether to enable side channel mitigation. When set to `true`, all side channel mitigations are enabled on your Azure HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. +* `side_channel_mitigation_enabled` - (Optional) Whether to enable side channel mitigation. Possible values are `true` and `false`. When set to `true`, all side channel mitigations are enabled on your Azure HCI cluster. Defaults to `true`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `smb_cluster_encryption_enabled` - (Required) Whether to enable SMB cluster encryption. When set to `true`, cluster east-west traffic is encrypted. Changing this forces a new Stack HCI Deployment Setting to be created. +* `smb_cluster_encryption_enabled` - (Optional) Whether to enable SMB cluster encryption. Possible values are `true` and `false`. When set to `true`, cluster east-west traffic is encrypted. Defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `smb_signing_enabled` - (Required) Whether to enable SMB signing. When set to `true`, the SMB default instance requires sign in for the client and server services. Changing this forces a new Stack HCI Deployment Setting to be created. +* `smb_signing_enabled` - (Optional) Whether to enable SMB signing. Possible values are `true` and `false`. When set to `true`, the SMB default instance requires sign in for the client and server services. Defaults to `true`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `wdac_enabled` - (Required) Whether to enable WDAC. When set to `true`, applications and the code that you can run on your Azure Stack HCI cluster are limited. Changing this forces a new Stack HCI Deployment Setting to be created. +* `wdac_enabled` - (Optional) Whether to enable WDAC. Possible values are `true` and `false`. When set to `true`, applications and the code that you can run on your Azure Stack HCI cluster are limited. Defaults to `true`. Changing this forces a new Stack HCI Deployment Setting to be created. --- @@ -553,15 +580,6 @@ A `storage_network` block supports the following: * `network_adapter_name` - (Required) The name of the network adapter. Changing this forces a new Stack HCI Deployment Setting to be created. * `vlan_id` - (Required) Specifies the ID for the VLAN storage network. This setting is applied to the network interfaces that route the storage and VM migration traffic. Changing this forces a new Stack HCI Deployment Setting to be created. - ---- - -A `virtual_switch_configuration_override` block supports the following: - -* `enable_iov` - (Optional) Specifies the IoV enable status for Virtual Switch. Changing this forces a new Stack HCI Deployment Setting to be created. - -* `load_balancing_algorithm` - (Optional) Specifies the load balancing algorithm for Virtual Switch. Changing this forces a new Stack HCI Deployment Setting to be created. - ## Attributes Reference In addition to the Arguments listed above - the following Attributes are exported: From c9bf9eb3142cfa8ef03072942a699a25309df8e4 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com.> Date: Tue, 21 May 2024 17:06:39 +0800 Subject: [PATCH 03/16] delete arc bridge before custom location --- .../stack_hci_deployment_setting_resource.go | 33 +++++++++++-------- ...ck_hci_deployment_setting_resource_test.go | 9 +++++ .../services/azurestackhci/testdata/ad.ps1 | 5 ++- .../azurestackhci/testdata/connect.ps1 | 5 ++- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go index a4068780aa73..60237ebb954e 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go @@ -3,6 +3,7 @@ package azurestackhci import ( "context" "fmt" + "log" "regexp" "strings" "time" @@ -812,7 +813,21 @@ func (StackHCIDeploymentSettingResource) Delete() sdk.ResourceFunc { return fmt.Errorf("deleting %s: %+v", id, err) } - if metadata.Client.Features.AzureStackHci.DeleteCustomLocationOnDestroy { + if metadata.Client.Features.AzureStackHci.DeleteArcBridgeOnDestroy && !response.WasNotFound(resp.HttpResponse) { + applianceName := fmt.Sprintf("%s-arcbridge", id.ClusterName) + applianceId := appliances.NewApplianceID(id.SubscriptionId, id.ResourceGroupName, applianceName) + + log.Printf("[DEBUG] Deleting Arc Resource Bridge Appliance %s generated during deployment", applianceId.ID()) + + applianceClient := metadata.Client.ArcResourceBridge.AppliancesClient + if err := applianceClient.DeleteThenPoll(ctx, applianceId); err != nil { + return fmt.Errorf("deleting %s: %+v", applianceId, err) + } + } + + if metadata.Client.Features.AzureStackHci.DeleteCustomLocationOnDestroy && !response.WasNotFound(resp.HttpResponse) { + log.Printf("[DEBUG] Deleting Custom Location and Storage Containers generated during deployment") + var customLocationName string if resp.Model != nil && resp.Model.Properties != nil && len(resp.Model.Properties.DeploymentConfiguration.ScaleUnits) > 0 && @@ -820,11 +835,11 @@ func (StackHCIDeploymentSettingResource) Delete() sdk.ResourceFunc { customLocationName = pointer.From(resp.Model.Properties.DeploymentConfiguration.ScaleUnits[0].DeploymentData.OptionalServices.CustomLocation) } - // try to delete the Custom Location generated during deployment if customLocationName != "" { customLocationId := customlocations.NewCustomLocationID(id.SubscriptionId, id.ResourceGroupName, customLocationName) - // try to delete the HCI Staroge Containers generated during deployment in the Custom Location + log.Printf("[DEBUG] Deleting Storage Containers under Custom Location %s", customLocationId.ID()) + storageContainerClient := metadata.Client.AzureStackHCI.StorageContainers resourceGroupId := commonids.NewResourceGroupID(id.SubscriptionId, id.ResourceGroupName) storageContainers, err := storageContainerClient.ListComplete(ctx, resourceGroupId) @@ -832,7 +847,7 @@ func (StackHCIDeploymentSettingResource) Delete() sdk.ResourceFunc { return fmt.Errorf("retrieving Stack HCI Storage Containers under %s: %+v", resourceGroupId.ID(), err) } - // find all Storage Containers under the Custom Location + // match Storage Containers under the Custom Location storageContainerNamePattern := regexp.MustCompile(`UserStorage[0-9]+-[a-z0-9]{32}`) for _, v := range storageContainers.Items { if v.Id != nil && v.ExtendedLocation != nil && v.ExtendedLocation.Name != nil && strings.EqualFold(*v.ExtendedLocation.Name, customLocationId.ID()) && v.Name != nil && storageContainerNamePattern.Match([]byte(*v.Name)) { @@ -854,16 +869,6 @@ func (StackHCIDeploymentSettingResource) Delete() sdk.ResourceFunc { } } - if metadata.Client.Features.AzureStackHci.DeleteArcBridgeOnDestroy { - // try to delete the Arc Resource Bridge Appliance generated during deployment - applianceName := fmt.Sprintf("%s-arcbridge", id.ClusterName) - applianceId := appliances.NewApplianceID(id.SubscriptionId, id.ResourceGroupName, applianceName) - applianceClient := metadata.Client.ArcResourceBridge.AppliancesClient - if err := applianceClient.DeleteThenPoll(ctx, applianceId); err != nil { - return fmt.Errorf("deleting %s: %+v", applianceId, err) - } - } - return nil }, } diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go index b72de12e03cd..1b7e6984e1ce 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go @@ -112,6 +112,9 @@ func (r StackHCIDeploymentSettingResource) basic(data acceptance.TestData) strin provider "azurerm" { features { + resource_group { + prevent_deletion_if_contains_resources = true + } azure_stack_hci { delete_arc_bridge_on_destroy = true delete_custom_location_on_destroy = true @@ -232,6 +235,9 @@ func (r StackHCIDeploymentSettingResource) requiresImport(data acceptance.TestDa provider "azurerm" { features { + resource_group { + prevent_deletion_if_contains_resources = true + } azure_stack_hci { delete_arc_bridge_on_destroy = true delete_custom_location_on_destroy = true @@ -255,6 +261,9 @@ func (r StackHCIDeploymentSettingResource) complete(data acceptance.TestData) st provider "azurerm" { features { + resource_group { + prevent_deletion_if_contains_resources = true + } azure_stack_hci { delete_arc_bridge_on_destroy = true delete_custom_location_on_destroy = true diff --git a/internal/services/azurestackhci/testdata/ad.ps1 b/internal/services/azurestackhci/testdata/ad.ps1 index 5bf9d14048a5..6f3639269946 100644 --- a/internal/services/azurestackhci/testdata/ad.ps1 +++ b/internal/services/azurestackhci/testdata/ad.ps1 @@ -77,7 +77,6 @@ for ($count = 0; $count -lt 6; $count++) { } } -if ($count -ge 3) { - throw "Failed to provision AD after 3 retries." +if ($count -ge 6) { + throw "Failed to provision AD after 6 retries." } - diff --git a/internal/services/azurestackhci/testdata/connect.ps1 b/internal/services/azurestackhci/testdata/connect.ps1 index e67d595be68a..69255362b35c 100644 --- a/internal/services/azurestackhci/testdata/connect.ps1 +++ b/internal/services/azurestackhci/testdata/connect.ps1 @@ -99,7 +99,6 @@ for ($count = 0; $count -lt 6; $count++) { throw "Arc server connection failed" } - sleep 600 $ready = $false while (!$ready) { Connect-AzAccount -Subscription $subscriptionId -Tenant $tenant -Credential $creds -ServicePrincipal @@ -127,6 +126,6 @@ for ($count = 0; $count -lt 6; $count++) { } } -if ($count -ge 3) { - throw "Failed to connect Arc server after 3 retries." +if ($count -ge 6) { + throw "Failed to connect Arc server after 6 retries." } From 8ed9bd225b2fd9931901f4a99328a23d4fda3feb Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com.> Date: Tue, 21 May 2024 17:08:47 +0800 Subject: [PATCH 04/16] terrafmt --- .../stack_hci_deployment_setting_resource_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go index 1b7e6984e1ce..3eeb21fa9650 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go @@ -112,9 +112,9 @@ func (r StackHCIDeploymentSettingResource) basic(data acceptance.TestData) strin provider "azurerm" { features { - resource_group { + resource_group { prevent_deletion_if_contains_resources = true - } + } azure_stack_hci { delete_arc_bridge_on_destroy = true delete_custom_location_on_destroy = true @@ -235,9 +235,9 @@ func (r StackHCIDeploymentSettingResource) requiresImport(data acceptance.TestDa provider "azurerm" { features { - resource_group { - prevent_deletion_if_contains_resources = true - } + resource_group { + prevent_deletion_if_contains_resources = true + } azure_stack_hci { delete_arc_bridge_on_destroy = true delete_custom_location_on_destroy = true @@ -261,9 +261,9 @@ func (r StackHCIDeploymentSettingResource) complete(data acceptance.TestData) st provider "azurerm" { features { - resource_group { + resource_group { prevent_deletion_if_contains_resources = true - } + } azure_stack_hci { delete_arc_bridge_on_destroy = true delete_custom_location_on_destroy = true From fdbf6d486645973ccb2f9ed594c5b667542fac20 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com.> Date: Wed, 29 May 2024 10:00:03 +0800 Subject: [PATCH 05/16] fix import test --- ...ck_hci_deployment_setting_resource_test.go | 136 +++++++++++++++--- ...stack_hci_deployment_setting.html.markdown | 2 +- 2 files changed, 121 insertions(+), 17 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go index 3eeb21fa9650..3c36faf444ed 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go @@ -113,7 +113,7 @@ func (r StackHCIDeploymentSettingResource) basic(data acceptance.TestData) strin provider "azurerm" { features { resource_group { - prevent_deletion_if_contains_resources = true + prevent_deletion_if_contains_resources = false } azure_stack_hci { delete_arc_bridge_on_destroy = true @@ -233,23 +233,94 @@ func (r StackHCIDeploymentSettingResource) requiresImport(data acceptance.TestDa return fmt.Sprintf(` %s -provider "azurerm" { - features { - resource_group { - prevent_deletion_if_contains_resources = true +resource "azurerm_stack_hci_deployment_setting" "import" { + stack_hci_cluster_id = azurerm_stack_hci_cluster.test.id + arc_resource_ids = azurerm_stack_hci_deployment_setting.test.arc_resource_ids + version = azurerm_stack_hci_deployment_setting.test.version + + scale_unit { + adou_path = azurerm_stack_hci_deployment_setting.test.scale_unit.0.adou_path + domain_fqdn = azurerm_stack_hci_deployment_setting.test.scale_unit.0.domain_fqdn + secrets_location = azurerm_stack_hci_deployment_setting.test.scale_unit.0.secrets_location + naming_prefix = azurerm_stack_hci_deployment_setting.test.scale_unit.0.naming_prefix + streaming_data_client_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.streaming_data_client_enabled + eu_location_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.eu_location_enabled + episodic_data_upload_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.episodic_data_upload_enabled + + bitlocker_boot_volume_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.bitlocker_boot_volume_enabled + bitlocker_data_volume_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.bitlocker_data_volume_enabled + credential_guard_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.credential_guard_enabled + drift_control_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.drift_control_enabled + drtm_protection_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.drtm_protection_enabled + hvci_protection_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.hvci_protection_enabled + side_channel_mitigation_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.side_channel_mitigation_enabled + smb_cluster_encryption_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.smb_cluster_encryption_enabled + smb_signing_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.smb_signing_enabled + wdac_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.wdac_enabled + + + cluster { + azure_service_endpoint = azurerm_stack_hci_deployment_setting.test.scale_unit.0.cluster.0.azure_service_endpoint + cloud_account_name = azurerm_stack_hci_deployment_setting.test.scale_unit.0.cluster.0.cloud_account_name + name = azurerm_stack_hci_deployment_setting.test.scale_unit.0.cluster.0.name + witness_type = azurerm_stack_hci_deployment_setting.test.scale_unit.0.cluster.0.witness_type + witness_path = azurerm_stack_hci_deployment_setting.test.scale_unit.0.cluster.0.witness_path } - azure_stack_hci { - delete_arc_bridge_on_destroy = true - delete_custom_location_on_destroy = true + + host_network { + intent { + name = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.0.name + adapter = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.0.adapter + traffic_type = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.0.traffic_type + } + + intent { + name = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.1.name + adapter = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.1.adapter + traffic_type = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.1.traffic_type + } + + storage_network { + name = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.storage_network.0.name + network_adapter_name = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.storage_network.0.network_adapter_name + vlan_id = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.storage_network.0.vlan_id + } + + storage_network { + name = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.storage_network.1.name + network_adapter_name = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.storage_network.1.network_adapter_name + vlan_id = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.storage_network.1.vlan_id + } } - } -} -resource "azurerm_stack_hci_deployment_setting" "import" { - stack_hci_cluster_id = azurerm_stack_hci_deployment_setting.test.stack_hci_cluster_id - version = azurerm_stack_hci_deployment_setting.test.version - arc_resource_ids = azurerm_stack_hci_deployment_setting.test.arc_resource_ids - scale_unit = azurerm_stack_hci_deployment_setting.test.scale_unit + infrastructure_network { + gateway = azurerm_stack_hci_deployment_setting.test.scale_unit.0.infrastructure_network.0.gateway + subnet_mask = azurerm_stack_hci_deployment_setting.test.scale_unit.0.infrastructure_network.0.subnet_mask + dns_server = azurerm_stack_hci_deployment_setting.test.scale_unit.0.infrastructure_network.0.dns_server + ip_pool { + ending_address = azurerm_stack_hci_deployment_setting.test.scale_unit.0.infrastructure_network.0.ip_pool.0.ending_address + starting_address = azurerm_stack_hci_deployment_setting.test.scale_unit.0.infrastructure_network.0.ip_pool.0.starting_address + } + } + + optional_service { + custom_location = azurerm_stack_hci_deployment_setting.test.scale_unit.0.optional_service.0.custom_location + } + + physical_node { + ipv4_address = azurerm_stack_hci_deployment_setting.test.scale_unit.0.physical_node.0.ipv4_address + name = azurerm_stack_hci_deployment_setting.test.scale_unit.0.physical_node.0.name + } + + physical_node { + ipv4_address = azurerm_stack_hci_deployment_setting.test.scale_unit.0.physical_node.1.ipv4_address + name = azurerm_stack_hci_deployment_setting.test.scale_unit.0.physical_node.1.name + } + + storage { + configuration_mode = azurerm_stack_hci_deployment_setting.test.scale_unit.0.storage.0.configuration_mode + } + } } `, config) } @@ -262,7 +333,7 @@ func (r StackHCIDeploymentSettingResource) complete(data acceptance.TestData) st provider "azurerm" { features { resource_group { - prevent_deletion_if_contains_resources = true + prevent_deletion_if_contains_resources = false } azure_stack_hci { delete_arc_bridge_on_destroy = true @@ -507,6 +578,35 @@ resource "azurerm_network_interface" "test" { } } +resource "azurerm_network_interface_security_group_association" "test" { + network_interface_id = azurerm_network_interface.test.id + network_security_group_id = azurerm_network_security_group.test.id + depends_on = [ + azurerm_network_interface.test, + azurerm_network_security_group.test, + ] +} + +resource "azurerm_network_security_group" "test" { + name = "nsg-${var.random_string}" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_network_security_rule" "test1" { + access = "Allow" + destination_address_prefix = "*" + destination_port_ranges = ["6985", "15985", "25985"] + direction = "Inbound" + name = "rule1-${var.random_string}" + network_security_group_name = azurerm_network_security_group.test.name + priority = 103 + protocol = "*" + resource_group_name = azurerm_resource_group.test.name + source_address_prefix = "*" + source_port_range = "*" +} + resource "azurerm_virtual_machine" "test" { name = "vm-${var.random_string}" resource_group_name = azurerm_resource_group.test.name @@ -527,6 +627,10 @@ resource "azurerm_virtual_machine" "test" { create_option = "FromImage" name = "vm-${var.random_string}_OsDisk" } + + depends_on = [ + azurerm_network_interface_security_group_association.test, + ] } locals { diff --git a/website/docs/r/stack_hci_deployment_setting.html.markdown b/website/docs/r/stack_hci_deployment_setting.html.markdown index 69e75789d00d..b9ab11f5c382 100644 --- a/website/docs/r/stack_hci_deployment_setting.html.markdown +++ b/website/docs/r/stack_hci_deployment_setting.html.markdown @@ -13,7 +13,6 @@ Manages a Stack HCI Deployment Setting. -> Note: Completion of the prerequisites of deploying the Azure Stack HCI in your environment is outside the scope of this document. For more details refer to the [Azure Stack HCI deployment sequence](https://learn.microsoft.com/en-us/azure-stack/hci/deploy/deployment-introduction#deployment-sequence). If you encounter issues completing the prerequisites, we'd recommend opening a ticket with Microsoft Support. -> Note: The Azure Provider include a Feature Toggle `delete_arc_bridge_on_destroy` which controls whether to delete Arc Resource Bridge generated on destroy, and Feature Toggle `delete_custom_location_on_destroy` which controls whether to delete Custom Location on destroy. The Provider will not do the deletion by default. See [the Features block documentation](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/features-block) for more information on Feature Toggles within Terraform. - ## Example Usage @@ -580,6 +579,7 @@ A `storage_network` block supports the following: * `network_adapter_name` - (Required) The name of the network adapter. Changing this forces a new Stack HCI Deployment Setting to be created. * `vlan_id` - (Required) Specifies the ID for the VLAN storage network. This setting is applied to the network interfaces that route the storage and VM migration traffic. Changing this forces a new Stack HCI Deployment Setting to be created. + ## Attributes Reference In addition to the Arguments listed above - the following Attributes are exported: From d40ccbeef4e7bf4c27345f2877460836f54f6e30 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com.> Date: Thu, 6 Jun 2024 10:21:25 +0800 Subject: [PATCH 06/16] disable network direct in acctest --- .../stack_hci_deployment_setting_resource_test.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go index 3c36faf444ed..ebd50fb6e136 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go @@ -381,7 +381,7 @@ resource "azurerm_stack_hci_deployment_setting" "test" { intent { name = "ManagementCompute" - override_adapter_property_enabled = false + override_adapter_property_enabled = true override_qos_policy_enabled = false override_virtual_switch_configuration_enabled = false adapter = [ @@ -398,15 +398,13 @@ resource "azurerm_stack_hci_deployment_setting" "test" { bandwidth_percentage_smb = "50" } override_adapter_property { - jumbo_packet = "9014" - network_direct = "Disabled" - network_direct_technology = "RoCEv2" + network_direct = "Disabled" } } intent { name = "Storage" - override_adapter_property_enabled = false + override_adapter_property_enabled = true override_qos_policy_enabled = false override_virtual_switch_configuration_enabled = false adapter = [ @@ -422,9 +420,7 @@ resource "azurerm_stack_hci_deployment_setting" "test" { bandwidth_percentage_smb = "50" } override_adapter_property { - jumbo_packet = "9014" - network_direct = "Enabled" - network_direct_technology = "RoCEv2" + network_direct = "Disabled" } } From 53acd4122f06425154261a0240bd4876e4b5fb2a Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Tue, 18 Jun 2024 09:04:44 +0000 Subject: [PATCH 07/16] rename override properties --- .../stack_hci_deployment_setting_resource.go | 104 +++++++++--------- ...ck_hci_deployment_setting_resource_test.go | 26 ++--- ...stack_hci_deployment_setting.html.markdown | 82 +++++++------- 3 files changed, 109 insertions(+), 103 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go index 60237ebb954e..8710d24ddaec 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go @@ -47,7 +47,7 @@ type ScaleUnitModel struct { DomainFqdn string `tfschema:"domain_fqdn"` HostNetwork []HostNetworkModel `tfschema:"host_network"` InfrastructureNetwork []InfrastructureNetworkModel `tfschema:"infrastructure_network"` - NamingPrefix string `tfschema:"naming_prefix"` + NamePrefix string `tfschema:"name_prefix"` OptionalService []OptionalServiceModel `tfschema:"optional_service"` PhysicalNode []PhysicalNodeModel `tfschema:"physical_node"` SecretsLocation string `tfschema:"secrets_location"` @@ -88,29 +88,29 @@ type HostNetworkModel struct { type HostNetworkIntentModel struct { Adapter []string `tfschema:"adapter"` + AdapterPropertyOverride []HostNetworkIntentAdapterPropertyOverrideModel `tfschema:"adapter_property_override"` + AdapterPropertyOverrideEnabled bool `tfschema:"adapter_property_override_enabled"` Name string `tfschema:"name"` - OverrideAdapterProperty []OverrideHostNetworkIntentAdapterPropertyModel `tfschema:"override_adapter_property"` - OverrideAdapterPropertyEnabled bool `tfschema:"override_adapter_property_enabled"` - OverrideQosPolicy []OverrideHostNetworkIntentQosPolicyModel `tfschema:"override_qos_policy"` - OverrideQosPolicyEnabled bool `tfschema:"override_qos_policy_enabled"` - OverrideVirtualSwitchConfiguration []OverrideHostNetworkIntentVirtualSwitchConfigurationModel `tfschema:"override_virtual_switch_configuration"` - OverrideVirtualSwitchConfigurationEnabled bool `tfschema:"override_virtual_switch_configuration_enabled"` + QosPolicyOverride []HostNetworkIntentQosPolicyOverrideModel `tfschema:"qos_policy_override"` + QosPolicyOverrideEnabled bool `tfschema:"qos_policy_override_enabled"` TrafficType []string `tfschema:"traffic_type"` + VirtualSwitchConfigurationOverride []HostNetworkIntentVirtualSwitchConfigurationOverrideModel `tfschema:"virtual_switch_configuration_override"` + VirtualSwitchConfigurationOverrideEnabled bool `tfschema:"virtual_switch_configuration_override_enabled"` } -type OverrideHostNetworkIntentAdapterPropertyModel struct { +type HostNetworkIntentAdapterPropertyOverrideModel struct { JumboPacket string `tfschema:"jumbo_packet"` NetworkDirect string `tfschema:"network_direct"` NetworkDirectTechnology string `tfschema:"network_direct_technology"` } -type OverrideHostNetworkIntentQosPolicyModel struct { +type HostNetworkIntentQosPolicyOverrideModel struct { BandWidthPercentageSMB string `tfschema:"bandwidth_percentage_smb"` PriorityValue8021ActionCluster string `tfschema:"priority_value8021_action_cluster"` PriorityValue8021ActionSMB string `tfschema:"priority_value8021_action_smb"` } -type OverrideHostNetworkIntentVirtualSwitchConfigurationModel struct { +type HostNetworkIntentVirtualSwitchConfigurationOverrideModel struct { EnableIov string `tfschema:"enable_iov"` LoadBalancingAlgorithm string `tfschema:"load_balancing_algorithm"` } @@ -294,7 +294,7 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem }, }, - "override_adapter_property": { + "adapter_property_override": { Type: pluginsdk.TypeList, Optional: true, ForceNew: true, @@ -325,14 +325,14 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem }, }, - "override_adapter_property_enabled": { + "adapter_property_override_enabled": { Type: pluginsdk.TypeBool, Optional: true, ForceNew: true, Default: false, }, - "override_qos_policy": { + "qos_policy_override": { Type: pluginsdk.TypeList, Optional: true, ForceNew: true, @@ -363,14 +363,14 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem }, }, - "override_qos_policy_enabled": { + "qos_policy_override_enabled": { Type: pluginsdk.TypeBool, Optional: true, ForceNew: true, Default: false, }, - "override_virtual_switch_configuration": { + "virtual_switch_configuration_override": { Type: pluginsdk.TypeList, Optional: true, ForceNew: true, @@ -394,7 +394,7 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem }, }, - "override_virtual_switch_configuration_enabled": { + "virtual_switch_configuration_override_enabled": { Type: pluginsdk.TypeBool, Optional: true, ForceNew: true, @@ -518,7 +518,7 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem }, }, - "naming_prefix": { + "name_prefix": { Type: pluginsdk.TypeString, Required: true, ForceNew: true, @@ -733,12 +733,18 @@ func (r StackHCIDeploymentSettingResource) Create() sdk.ResourceFunc { }, } + // do validation + future, err := client.CreateOrUpdate(ctx, id, payload) + if err != nil { + return fmt.Errorf("validate %s: %+v", id, err) + } + // the resource may exist even validation error metadata.SetID(id) - // do validation - if err := client.CreateOrUpdateThenPoll(ctx, id, payload); err != nil { - return fmt.Errorf("creating %s: %+v", id, err) + // poll validation + if err := future.Poller.PollUntilDone(ctx); err != nil { + return fmt.Errorf("polling after validate %s: %+v", id, err) } // do deployment @@ -888,7 +894,7 @@ func ExpandDeploymentSettingScaleUnits(input []ScaleUnitModel) []deploymentsetti DomainFqdn: pointer.To(item.DomainFqdn), HostNetwork: ExpandDeploymentSettingHostNetwork(item.HostNetwork), InfrastructureNetwork: ExpandDeploymentSettingInfrastructureNetwork(item.InfrastructureNetwork), - NamingPrefix: pointer.To(item.NamingPrefix), + NamingPrefix: pointer.To(item.NamePrefix), Observability: ExpandDeploymentSettingObservability(item), OptionalServices: ExpandDeploymentSettingOptionalService(item.OptionalService), PhysicalNodes: ExpandDeploymentSettingPhysicalNode(item.PhysicalNode), @@ -915,7 +921,7 @@ func FlattenDeploymentSettingScaleUnits(input []deploymentsettings.ScaleUnits) [ DomainFqdn: pointer.From(item.DeploymentData.DomainFqdn), HostNetwork: FlattenDeploymentSettingHostNetwork(item.DeploymentData.HostNetwork), InfrastructureNetwork: FlattenDeploymentSettingInfrastructureNetwork(item.DeploymentData.InfrastructureNetwork), - NamingPrefix: pointer.From(item.DeploymentData.NamingPrefix), + NamePrefix: pointer.From(item.DeploymentData.NamingPrefix), OptionalService: FlattenDeploymentSettingOptionalService(item.DeploymentData.OptionalServices), PhysicalNode: FlattenDeploymentSettingPhysicalNode(item.DeploymentData.PhysicalNodes), SecretsLocation: pointer.From(item.DeploymentData.SecretsLocation), @@ -1014,14 +1020,14 @@ func ExpandDeploymentSettingHostNetworkIntent(input []HostNetworkIntentModel) *[ for _, item := range input { results = append(results, deploymentsettings.Intents{ Adapter: pointer.To(item.Adapter), - AdapterPropertyOverrides: ExpandHostNetworkIntentAdapterPropertyOverride(item.OverrideAdapterProperty), + AdapterPropertyOverrides: ExpandHostNetworkIntentAdapterPropertyOverride(item.AdapterPropertyOverride), Name: pointer.To(item.Name), - OverrideAdapterProperty: pointer.To(item.OverrideAdapterPropertyEnabled), - OverrideQosPolicy: pointer.To(item.OverrideQosPolicyEnabled), - OverrideVirtualSwitchConfiguration: pointer.To(item.OverrideVirtualSwitchConfigurationEnabled), - QosPolicyOverrides: ExpandHostNetworkIntentQosPolicyOverride(item.OverrideQosPolicy), + OverrideAdapterProperty: pointer.To(item.AdapterPropertyOverrideEnabled), + OverrideQosPolicy: pointer.To(item.QosPolicyOverrideEnabled), + OverrideVirtualSwitchConfiguration: pointer.To(item.VirtualSwitchConfigurationOverrideEnabled), + QosPolicyOverrides: ExpandHostNetworkIntentQosPolicyOverride(item.QosPolicyOverride), TrafficType: pointer.To(item.TrafficType), - VirtualSwitchConfigurationOverrides: ExpandHostNetworkIntentVirtualSwitchConfigurationOverride(item.OverrideVirtualSwitchConfiguration), + VirtualSwitchConfigurationOverrides: ExpandHostNetworkIntentVirtualSwitchConfigurationOverride(item.VirtualSwitchConfigurationOverride), }) } @@ -1037,21 +1043,21 @@ func FlattenDeploymentSettingHostNetworkIntent(input *[]deploymentsettings.Inten for _, item := range *input { results = append(results, HostNetworkIntentModel{ Adapter: pointer.From(item.Adapter), - OverrideAdapterProperty: FlattenHostNetworkIntentAdapterPropertyOverride(item.AdapterPropertyOverrides), + AdapterPropertyOverride: FlattenHostNetworkIntentAdapterPropertyOverride(item.AdapterPropertyOverrides), Name: pointer.From(item.Name), - OverrideAdapterPropertyEnabled: pointer.From(item.OverrideAdapterProperty), - OverrideQosPolicyEnabled: pointer.From(item.OverrideQosPolicy), - OverrideVirtualSwitchConfigurationEnabled: pointer.From(item.OverrideVirtualSwitchConfiguration), - OverrideQosPolicy: FlattenHostNetworkIntentQosPolicyOverride(item.QosPolicyOverrides), + AdapterPropertyOverrideEnabled: pointer.From(item.OverrideAdapterProperty), + QosPolicyOverrideEnabled: pointer.From(item.OverrideQosPolicy), + VirtualSwitchConfigurationOverrideEnabled: pointer.From(item.OverrideVirtualSwitchConfiguration), + QosPolicyOverride: FlattenHostNetworkIntentQosPolicyOverride(item.QosPolicyOverrides), TrafficType: pointer.From(item.TrafficType), - OverrideVirtualSwitchConfiguration: FlattenHostNetworkIntentVirtualSwitchConfigurationOverride(item.VirtualSwitchConfigurationOverrides), + VirtualSwitchConfigurationOverride: FlattenHostNetworkIntentVirtualSwitchConfigurationOverride(item.VirtualSwitchConfigurationOverrides), }) } return results } -func ExpandHostNetworkIntentAdapterPropertyOverride(input []OverrideHostNetworkIntentAdapterPropertyModel) *deploymentsettings.AdapterPropertyOverrides { +func ExpandHostNetworkIntentAdapterPropertyOverride(input []HostNetworkIntentAdapterPropertyOverrideModel) *deploymentsettings.AdapterPropertyOverrides { if len(input) == 0 { return &deploymentsettings.AdapterPropertyOverrides{ JumboPacket: pointer.To(""), @@ -1069,9 +1075,9 @@ func ExpandHostNetworkIntentAdapterPropertyOverride(input []OverrideHostNetworkI } } -func FlattenHostNetworkIntentAdapterPropertyOverride(input *deploymentsettings.AdapterPropertyOverrides) []OverrideHostNetworkIntentAdapterPropertyModel { +func FlattenHostNetworkIntentAdapterPropertyOverride(input *deploymentsettings.AdapterPropertyOverrides) []HostNetworkIntentAdapterPropertyOverrideModel { if input == nil { - return make([]OverrideHostNetworkIntentAdapterPropertyModel, 0) + return make([]HostNetworkIntentAdapterPropertyOverrideModel, 0) } jumboPacket := pointer.From(input.JumboPacket) @@ -1080,17 +1086,17 @@ func FlattenHostNetworkIntentAdapterPropertyOverride(input *deploymentsettings.A // server will return the block with empty string in all fields by default if jumboPacket == "" && networkDirect == "" && networkDirectTechnology == "" { - return make([]OverrideHostNetworkIntentAdapterPropertyModel, 0) + return make([]HostNetworkIntentAdapterPropertyOverrideModel, 0) } - return []OverrideHostNetworkIntentAdapterPropertyModel{{ + return []HostNetworkIntentAdapterPropertyOverrideModel{{ JumboPacket: jumboPacket, NetworkDirect: networkDirect, NetworkDirectTechnology: networkDirectTechnology, }} } -func ExpandHostNetworkIntentQosPolicyOverride(input []OverrideHostNetworkIntentQosPolicyModel) *deploymentsettings.QosPolicyOverrides { +func ExpandHostNetworkIntentQosPolicyOverride(input []HostNetworkIntentQosPolicyOverrideModel) *deploymentsettings.QosPolicyOverrides { if len(input) == 0 { return &deploymentsettings.QosPolicyOverrides{ BandwidthPercentageSMB: pointer.To(""), @@ -1108,9 +1114,9 @@ func ExpandHostNetworkIntentQosPolicyOverride(input []OverrideHostNetworkIntentQ } } -func FlattenHostNetworkIntentQosPolicyOverride(input *deploymentsettings.QosPolicyOverrides) []OverrideHostNetworkIntentQosPolicyModel { +func FlattenHostNetworkIntentQosPolicyOverride(input *deploymentsettings.QosPolicyOverrides) []HostNetworkIntentQosPolicyOverrideModel { if input == nil { - return make([]OverrideHostNetworkIntentQosPolicyModel, 0) + return make([]HostNetworkIntentQosPolicyOverrideModel, 0) } bandwidthPercentageSMB := pointer.From(input.BandwidthPercentageSMB) @@ -1119,17 +1125,17 @@ func FlattenHostNetworkIntentQosPolicyOverride(input *deploymentsettings.QosPoli // server will return the block with empty string in all fields by default if bandwidthPercentageSMB == "" && priorityValue8021ActionCluster == "" && priorityValue8021ActionSMB == "" { - return make([]OverrideHostNetworkIntentQosPolicyModel, 0) + return make([]HostNetworkIntentQosPolicyOverrideModel, 0) } - return []OverrideHostNetworkIntentQosPolicyModel{{ + return []HostNetworkIntentQosPolicyOverrideModel{{ BandWidthPercentageSMB: bandwidthPercentageSMB, PriorityValue8021ActionCluster: priorityValue8021ActionCluster, PriorityValue8021ActionSMB: priorityValue8021ActionSMB, }} } -func ExpandHostNetworkIntentVirtualSwitchConfigurationOverride(input []OverrideHostNetworkIntentVirtualSwitchConfigurationModel) *deploymentsettings.VirtualSwitchConfigurationOverrides { +func ExpandHostNetworkIntentVirtualSwitchConfigurationOverride(input []HostNetworkIntentVirtualSwitchConfigurationOverrideModel) *deploymentsettings.VirtualSwitchConfigurationOverrides { if len(input) == 0 { return &deploymentsettings.VirtualSwitchConfigurationOverrides{ EnableIov: pointer.To(""), @@ -1145,9 +1151,9 @@ func ExpandHostNetworkIntentVirtualSwitchConfigurationOverride(input []OverrideH } } -func FlattenHostNetworkIntentVirtualSwitchConfigurationOverride(input *deploymentsettings.VirtualSwitchConfigurationOverrides) []OverrideHostNetworkIntentVirtualSwitchConfigurationModel { +func FlattenHostNetworkIntentVirtualSwitchConfigurationOverride(input *deploymentsettings.VirtualSwitchConfigurationOverrides) []HostNetworkIntentVirtualSwitchConfigurationOverrideModel { if input == nil { - return make([]OverrideHostNetworkIntentVirtualSwitchConfigurationModel, 0) + return make([]HostNetworkIntentVirtualSwitchConfigurationOverrideModel, 0) } enableIov := pointer.From(input.EnableIov) @@ -1155,10 +1161,10 @@ func FlattenHostNetworkIntentVirtualSwitchConfigurationOverride(input *deploymen // server will return the block with empty string in all fields by default if enableIov == "" && loadBalancingAlgorithm == "" { - return make([]OverrideHostNetworkIntentVirtualSwitchConfigurationModel, 0) + return make([]HostNetworkIntentVirtualSwitchConfigurationOverrideModel, 0) } - return []OverrideHostNetworkIntentVirtualSwitchConfigurationModel{{ + return []HostNetworkIntentVirtualSwitchConfigurationOverrideModel{{ EnableIov: enableIov, LoadBalancingAlgorithm: loadBalancingAlgorithm, }} diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go index ebd50fb6e136..35826323f19a 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go @@ -131,7 +131,7 @@ resource "azurerm_stack_hci_deployment_setting" "test" { adou_path = "OU=hci${var.random_string},DC=jumpstart,DC=local" domain_fqdn = "jumpstart.local" secrets_location = azurerm_key_vault.DeploymentKeyVault.vault_uri - naming_prefix = "hci${var.random_string}" + name_prefix = "hci${var.random_string}" streaming_data_client_enabled = true eu_location_enabled = false episodic_data_upload_enabled = true @@ -242,7 +242,7 @@ resource "azurerm_stack_hci_deployment_setting" "import" { adou_path = azurerm_stack_hci_deployment_setting.test.scale_unit.0.adou_path domain_fqdn = azurerm_stack_hci_deployment_setting.test.scale_unit.0.domain_fqdn secrets_location = azurerm_stack_hci_deployment_setting.test.scale_unit.0.secrets_location - naming_prefix = azurerm_stack_hci_deployment_setting.test.scale_unit.0.naming_prefix + name_prefix = azurerm_stack_hci_deployment_setting.test.scale_unit.0.name_prefix streaming_data_client_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.streaming_data_client_enabled eu_location_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.eu_location_enabled episodic_data_upload_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.episodic_data_upload_enabled @@ -351,7 +351,7 @@ resource "azurerm_stack_hci_deployment_setting" "test" { adou_path = "OU=hci${var.random_string},DC=jumpstart,DC=local" domain_fqdn = "jumpstart.local" secrets_location = azurerm_key_vault.DeploymentKeyVault.vault_uri - naming_prefix = "hci${var.random_string}" + name_prefix = "hci${var.random_string}" streaming_data_client_enabled = true eu_location_enabled = false episodic_data_upload_enabled = true @@ -381,9 +381,9 @@ resource "azurerm_stack_hci_deployment_setting" "test" { intent { name = "ManagementCompute" - override_adapter_property_enabled = true - override_qos_policy_enabled = false - override_virtual_switch_configuration_enabled = false + adapter_property_override_enabled = true + qos_policy_override_enabled = false + virtual_switch_configuration_override_enabled = false adapter = [ "FABRIC", "FABRIC2", @@ -392,21 +392,21 @@ resource "azurerm_stack_hci_deployment_setting" "test" { "Management", "Compute", ] - override_qos_policy { + qos_policy_override { priority_value8021_action_cluster = "7" priority_value8021_action_smb = "3" bandwidth_percentage_smb = "50" } - override_adapter_property { + adapter_property_override { network_direct = "Disabled" } } intent { name = "Storage" - override_adapter_property_enabled = true - override_qos_policy_enabled = false - override_virtual_switch_configuration_enabled = false + adapter_property_override_enabled = true + qos_policy_override_enabled = false + virtual_switch_configuration_override_enabled = false adapter = [ "StorageA", "StorageB", @@ -414,12 +414,12 @@ resource "azurerm_stack_hci_deployment_setting" "test" { traffic_type = [ "Storage", ] - override_qos_policy { + qos_policy_override { priority_value8021_action_cluster = "7" priority_value8021_action_smb = "3" bandwidth_percentage_smb = "50" } - override_adapter_property { + adapter_property_override { network_direct = "Disabled" } } diff --git a/website/docs/r/stack_hci_deployment_setting.html.markdown b/website/docs/r/stack_hci_deployment_setting.html.markdown index b9ab11f5c382..d55dae84fc0b 100644 --- a/website/docs/r/stack_hci_deployment_setting.html.markdown +++ b/website/docs/r/stack_hci_deployment_setting.html.markdown @@ -248,7 +248,7 @@ resource "azurerm_stack_hci_deployment_setting" "example" { adou_path = "OU=hci,DC=jumpstart,DC=local" domain_fqdn = "jumpstart.local" secrets_location = azurerm_key_vault.DeploymentKeyVault.vault_uri - naming_prefix = "hci" + name_prefix = "hci" cluster { azure_service_endpoint = "core.windows.net" @@ -263,9 +263,9 @@ resource "azurerm_stack_hci_deployment_setting" "example" { storage_connectivity_switchless_enabled = false intent { name = "ManagementCompute" - override_adapter_property_enabled = false - override_qos_policy_enabled = false - override_virtual_switch_configuration_enabled = false + adapter_property_override_enabled = false + qos_policy_override_enabled = false + virtual_switch_configuration_override_enabled = false adapter = [ "FABRIC", "FABRIC2", @@ -274,12 +274,12 @@ resource "azurerm_stack_hci_deployment_setting" "example" { "Management", "Compute", ] - override_qos_policy { + qos_policy_override { priority_value8021_action_cluster = "7" priority_value8021_action_smb = "3" bandwidth_percentage_smb = "50" } - override_adapter_property { + adapter_property_override { jumbo_packet = "9014" network_direct = "Disabled" network_direct_technology = "RoCEv2" @@ -288,9 +288,9 @@ resource "azurerm_stack_hci_deployment_setting" "example" { intent { name = "Storage" - override_adapter_property_enabled = false - override_qos_policy_enabled = false - override_virtual_switch_configuration_enabled = false + adapter_property_override_enabled = false + qos_policy_override_enabled = false + virtual_switch_configuration_override_enabled = false adapter = [ "StorageA", "StorageB", @@ -298,12 +298,12 @@ resource "azurerm_stack_hci_deployment_setting" "example" { traffic_type = [ "Storage", ] - override_qos_policy { + qos_policy_override { priority_value8021_action_cluster = "7" priority_value8021_action_smb = "3" bandwidth_percentage_smb = "50" } - override_adapter_property { + adapter_property_override { jumbo_packet = "9014" network_direct = "Enabled" network_direct_technology = "RoCEv2" @@ -390,6 +390,16 @@ The following arguments are supported: --- +A `adapter_property_override` block supports the following: + +* `jumbo_packet` - (Optional) The jumbo frame size of the adapter. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `network_direct` - (Optional) The network direct of the adapter. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `network_direct_technology` - (Optional) The network direct technology of the adapter. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. + +--- + A `cluster` block supports the following: * `azure_service_endpoint` - (Required) Specifies the Azure blob service endpoint, for example, `core.windows.net`. Changing this forces a new Stack HCI Deployment Setting to be created. @@ -410,7 +420,7 @@ A `host_network` block supports the following: * `storage_network` - (Required) One or more `storage_network` blocks as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. -* `storage_auto_ip_enabled` - (Optional) Whether allows users to specify IPs and Mask for Storage NICs when Network ATC is not assigning the IPs for storage automatically. Optional parameter required only for [3 Nodes Switchless deployments](https://learn.microsoft.com/azure-stack/hci/concepts/physical-network-requirements?tabs=overview%2C23H2reqs#using-switchless). Possible values are `true` and `false`. Defaults to `true`. Changing this forces a new Stack HCI Deployment Setting to be created. +* `storage_auto_ip_enabled` - (Optional) Whether allows users to specify IPs and Mask for Storage NICs when Network ATC is not assigning the IPs for storage automatically. Optional parameter required only for [3 nodes switchless deployments](https://learn.microsoft.com/azure-stack/hci/concepts/physical-network-requirements?tabs=overview%2C23H2reqs#using-switchless). Possible values are `true` and `false`. Defaults to `true`. Changing this forces a new Stack HCI Deployment Setting to be created. * `storage_connectivity_switchless_enabled` - (Optional) Defines how the storage adapters between nodes are connected either switch or switch less. Possible values are `true` and `false`. Defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. @@ -440,17 +450,17 @@ A `intent` block supports the following: * `traffic_type` - (Required) Specifies a list of network traffic types. Possible values are `Compute`, `Storage`, `Management`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `override_adapter_property` - (Optional) A `override_adapter_property` block as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. +* `adapter_property_override` - (Optional) A `adapter_property_override` block as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. -* `override_adapter_property_enabled` - (Optional) Whether to override adapter properties. Possible values are `true` and `false`. defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. +* `adapter_property_override_enabled` - (Optional) Whether to override adapter properties. Possible values are `true` and `false`. defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `override_qos_policy` - (Optional) A `override_qos_policy` block as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. +* `qos_policy_override` - (Optional) A `qos_policy_override` block as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. -* `override_qos_policy_enabled` - (Optional) Whether to override QoS policy. Possible values are `true` and `false`. defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. +* `qos_policy_override_enabled` - (Optional) Whether to override QoS policy. Possible values are `true` and `false`. defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `override_virtual_switch_configuration` - (Optional) A `override_virtual_switch_configuration` block as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. +* `virtual_switch_configuration_override` - (Optional) A `virtual_switch_configuration_override` block as defined below. Changing this forces a new Stack HCI Deployment Setting to be created. -* `override_virtual_switch_configuration_enabled` - (Optional) Whether to override virtual switch configuration. Possible values are `true` and `false`. defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. +* `virtual_switch_configuration_override_enabled` - (Optional) Whether to override virtual switch configuration. Possible values are `true` and `false`. defaults to `false`. Changing this forces a new Stack HCI Deployment Setting to be created. --- @@ -478,17 +488,15 @@ A `optional_service` block supports the following: --- -A `override_adapter_property` block supports the following: - -* `jumbo_packet` - (Optional) The jumbo frame size of the adapter. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. +A `physical_node` block supports the following: -* `network_direct` - (Optional) The network direct of the adapter. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. +* `ipv4_address` - (Required) Specifies the IPv4 address assigned to each physical server on your Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. -* `network_direct_technology` - (Optional) The network direct technology of the adapter. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. +* `name` - (Required) The NETBIOS name of each physical server on your Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. --- -A `override_qos_policy` block supports the following: +A `qos_policy_override` block supports the following: * `bandwidth_percentage_smb` - (Optional) Specifies the percentage of the allocated storage traffic bandwidth. This parameter should only be modified based on your OEM guidance. Changing this forces a new Stack HCI Deployment Setting to be created. @@ -498,22 +506,6 @@ A `override_qos_policy` block supports the following: --- -A `override_virtual_switch_configuration` block supports the following: - -* `enable_iov` - (Optional) Specifies the IoV enable status for Virtual Switch. Changing this forces a new Stack HCI Deployment Setting to be created. - -* `load_balancing_algorithm` - (Optional) Specifies the load balancing algorithm for Virtual Switch. Changing this forces a new Stack HCI Deployment Setting to be created. - ---- - -A `physical_node` block supports the following: - -* `ipv4_address` - (Required) Specifies the IPv4 address assigned to each physical server on your Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. - -* `name` - (Required) The NETBIOS name of each physical server on your Azure Stack HCI cluster. Changing this forces a new Stack HCI Deployment Setting to be created. - ---- - A `scale_unit` block supports the following: * `adou_path` - (Required) Specify the full name of the Active Directory Organizational Unit container object prepared for the deployment, including the domain components. For example:`OU=HCI01,DC=contoso,DC=com`. Changing this forces a new Stack HCI Deployment Setting to be created. @@ -526,7 +518,7 @@ A `scale_unit` block supports the following: * `infrastructure_network` - (Required) One or more `infrastructure_network` blocks as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. -* `naming_prefix` - (Required) Specifies the naming prefix to deploy cluster. It must be 1-8 characters long and contain only letters, numbers and hyphens Changing this forces a new Stack HCI Deployment Setting to be created. +* `name_prefix` - (Required) Specifies the name prefix to deploy cluster. It must be 1-8 characters long and contain only letters, numbers and hyphens Changing this forces a new Stack HCI Deployment Setting to be created. * `optional_service` - (Required) A `optional_service` block as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. @@ -580,6 +572,14 @@ A `storage_network` block supports the following: * `vlan_id` - (Required) Specifies the ID for the VLAN storage network. This setting is applied to the network interfaces that route the storage and VM migration traffic. Changing this forces a new Stack HCI Deployment Setting to be created. +--- + +A `virtual_switch_configuration_override` block supports the following: + +* `enable_iov` - (Optional) Specifies the IoV enable status for Virtual Switch. Changing this forces a new Stack HCI Deployment Setting to be created. + +* `load_balancing_algorithm` - (Optional) Specifies the load balancing algorithm for Virtual Switch. Changing this forces a new Stack HCI Deployment Setting to be created. + ## Attributes Reference In addition to the Arguments listed above - the following Attributes are exported: From f6414bebae83965912954101719ecd4690e600c2 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Wed, 19 Jun 2024 02:01:03 +0000 Subject: [PATCH 08/16] disable network direct in test --- ...ck_hci_deployment_setting_resource_test.go | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go index 35826323f19a..6628472a61fa 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go @@ -147,7 +147,6 @@ resource "azurerm_stack_hci_deployment_setting" "test" { smb_signing_enabled = true wdac_enabled = true - cluster { azure_service_endpoint = "core.windows.net" cloud_account_name = azurerm_storage_account.witness.name @@ -158,7 +157,11 @@ resource "azurerm_stack_hci_deployment_setting" "test" { host_network { intent { - name = "ManagementCompute" + name = "ManagementCompute" + adapter_property_override_enabled = true + adapter_property_override { + network_direct = "Disabled" + } adapter = [ "FABRIC", "FABRIC2", @@ -170,7 +173,11 @@ resource "azurerm_stack_hci_deployment_setting" "test" { } intent { - name = "Storage" + name = "Storage" + adapter_property_override_enabled = true + adapter_property_override { + network_direct = "Disabled" + } adapter = [ "StorageA", "StorageB", @@ -269,15 +276,23 @@ resource "azurerm_stack_hci_deployment_setting" "import" { host_network { intent { - name = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.0.name - adapter = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.0.adapter - traffic_type = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.0.traffic_type + name = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.0.name + adapter = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.0.adapter + adapter_property_override_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.0.adapter_property_override_enabled + traffic_type = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.0.traffic_type + adapter_property_override { + network_direct = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.0.adapter_property_override.0.network_direct + } } intent { - name = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.1.name - adapter = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.1.adapter - traffic_type = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.1.traffic_type + name = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.1.name + adapter = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.1.adapter + adapter_property_override_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.0.adapter_property_override_enabled + traffic_type = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.1.traffic_type + adapter_property_override { + network_direct = azurerm_stack_hci_deployment_setting.test.scale_unit.0.host_network.0.intent.0.adapter_property_override.0.network_direct + } } storage_network { From de64610123e0963e68759580ec44b0a7202791f8 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Mon, 24 Jun 2024 07:16:31 +0000 Subject: [PATCH 09/16] doc: elaborate on eu_location_enabled --- website/docs/r/stack_hci_deployment_setting.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/stack_hci_deployment_setting.html.markdown b/website/docs/r/stack_hci_deployment_setting.html.markdown index d55dae84fc0b..003fedccf885 100644 --- a/website/docs/r/stack_hci_deployment_setting.html.markdown +++ b/website/docs/r/stack_hci_deployment_setting.html.markdown @@ -476,7 +476,7 @@ A `observability` block supports the following: * `episodic_data_upload_enabled` - (Required) Whether to collect log data to facilitate quicker issue resolution. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. -* `eu_location_enabled` - (Required) Whether to store data sent to Microsoft in EU. The log and diagnostic data is sent to the appropriate diagnostics servers depending upon where your cluster resides. Setting this to `false` results in all data sent to Microsoft to be stored outside of the EU. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. +* `eu_location_enabled` - (Required) Whether to store log and diagnostic data sent to Microsoft in EU or outside of EU. The log and diagnostic data is sent to the appropriate diagnostics servers depending upon where your cluster resides. Setting this to `false` results in all data sent to Microsoft to be stored outside of EU. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. * `streaming_data_client_enabled` - (Required) Whether the telemetry data will be sent to Microsoft. Possible values are `true` and `false`. Changing this forces a new Stack HCI Deployment Setting to be created. From e97e1b7d09ef6f80ebd2a9ff382f7fbd41ae8623 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Fri, 28 Jun 2024 04:06:07 +0000 Subject: [PATCH 10/16] change expand/flatten func name first char to lowercase --- .../stack_hci_deployment_setting_resource.go | 112 +++++++++--------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go index 8710d24ddaec..cfbbf6221fdb 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go @@ -728,7 +728,7 @@ func (r StackHCIDeploymentSettingResource) Create() sdk.ResourceFunc { DeploymentMode: deploymentsettings.DeploymentModeValidate, DeploymentConfiguration: deploymentsettings.DeploymentConfiguration{ Version: pointer.To(config.Version), - ScaleUnits: ExpandDeploymentSettingScaleUnits(config.ScaleUnit), + ScaleUnits: expandDeploymentSettingScaleUnits(config.ScaleUnit), }, }, } @@ -786,7 +786,7 @@ func (StackHCIDeploymentSettingResource) Read() sdk.ResourceFunc { if props := model.Properties; props != nil { schema.ArcResourceIds = props.ArcNodeResourceIds schema.Version = pointer.From(props.DeploymentConfiguration.Version) - schema.ScaleUnit = FlattenDeploymentSettingScaleUnits(props.DeploymentConfiguration.ScaleUnits) + schema.ScaleUnit = flattenDeploymentSettingScaleUnits(props.DeploymentConfiguration.ScaleUnits) } } @@ -880,7 +880,7 @@ func (StackHCIDeploymentSettingResource) Delete() sdk.ResourceFunc { } } -func ExpandDeploymentSettingScaleUnits(input []ScaleUnitModel) []deploymentsettings.ScaleUnits { +func expandDeploymentSettingScaleUnits(input []ScaleUnitModel) []deploymentsettings.ScaleUnits { if len(input) == 0 { return nil } @@ -890,17 +890,17 @@ func ExpandDeploymentSettingScaleUnits(input []ScaleUnitModel) []deploymentsetti results = append(results, deploymentsettings.ScaleUnits{ DeploymentData: deploymentsettings.DeploymentData{ AdouPath: pointer.To(item.AdouPath), - Cluster: ExpandDeploymentSettingCluster(item.Cluster), + Cluster: expandDeploymentSettingCluster(item.Cluster), DomainFqdn: pointer.To(item.DomainFqdn), - HostNetwork: ExpandDeploymentSettingHostNetwork(item.HostNetwork), - InfrastructureNetwork: ExpandDeploymentSettingInfrastructureNetwork(item.InfrastructureNetwork), + HostNetwork: expandDeploymentSettingHostNetwork(item.HostNetwork), + InfrastructureNetwork: expandDeploymentSettingInfrastructureNetwork(item.InfrastructureNetwork), NamingPrefix: pointer.To(item.NamePrefix), - Observability: ExpandDeploymentSettingObservability(item), - OptionalServices: ExpandDeploymentSettingOptionalService(item.OptionalService), - PhysicalNodes: ExpandDeploymentSettingPhysicalNode(item.PhysicalNode), + Observability: expandDeploymentSettingObservability(item), + OptionalServices: expandDeploymentSettingOptionalService(item.OptionalService), + PhysicalNodes: expandDeploymentSettingPhysicalNode(item.PhysicalNode), SecretsLocation: pointer.To(item.SecretsLocation), - SecuritySettings: ExpandDeploymentSettingSecuritySetting(item), - Storage: ExpandDeploymentSettingStorage(item.Storage), + SecuritySettings: expandDeploymentSettingSecuritySetting(item), + Storage: expandDeploymentSettingStorage(item.Storage), }, }) } @@ -908,7 +908,7 @@ func ExpandDeploymentSettingScaleUnits(input []ScaleUnitModel) []deploymentsetti return results } -func FlattenDeploymentSettingScaleUnits(input []deploymentsettings.ScaleUnits) []ScaleUnitModel { +func flattenDeploymentSettingScaleUnits(input []deploymentsettings.ScaleUnits) []ScaleUnitModel { if len(input) == 0 { return make([]ScaleUnitModel, 0) } @@ -917,15 +917,15 @@ func FlattenDeploymentSettingScaleUnits(input []deploymentsettings.ScaleUnits) [ for _, item := range input { result := ScaleUnitModel{ AdouPath: pointer.From(item.DeploymentData.AdouPath), - Cluster: FlattenDeploymentSettingCluster(item.DeploymentData.Cluster), + Cluster: flattenDeploymentSettingCluster(item.DeploymentData.Cluster), DomainFqdn: pointer.From(item.DeploymentData.DomainFqdn), - HostNetwork: FlattenDeploymentSettingHostNetwork(item.DeploymentData.HostNetwork), - InfrastructureNetwork: FlattenDeploymentSettingInfrastructureNetwork(item.DeploymentData.InfrastructureNetwork), + HostNetwork: flattenDeploymentSettingHostNetwork(item.DeploymentData.HostNetwork), + InfrastructureNetwork: flattenDeploymentSettingInfrastructureNetwork(item.DeploymentData.InfrastructureNetwork), NamePrefix: pointer.From(item.DeploymentData.NamingPrefix), - OptionalService: FlattenDeploymentSettingOptionalService(item.DeploymentData.OptionalServices), - PhysicalNode: FlattenDeploymentSettingPhysicalNode(item.DeploymentData.PhysicalNodes), + OptionalService: flattenDeploymentSettingOptionalService(item.DeploymentData.OptionalServices), + PhysicalNode: flattenDeploymentSettingPhysicalNode(item.DeploymentData.PhysicalNodes), SecretsLocation: pointer.From(item.DeploymentData.SecretsLocation), - Storage: FlattenDeploymentSettingStorage(item.DeploymentData.Storage), + Storage: flattenDeploymentSettingStorage(item.DeploymentData.Storage), } if observability := item.DeploymentData.Observability; observability != nil { @@ -953,7 +953,7 @@ func FlattenDeploymentSettingScaleUnits(input []deploymentsettings.ScaleUnits) [ return results } -func ExpandDeploymentSettingCluster(input []ClusterModel) *deploymentsettings.DeploymentCluster { +func expandDeploymentSettingCluster(input []ClusterModel) *deploymentsettings.DeploymentCluster { if len(input) == 0 { return nil } @@ -969,7 +969,7 @@ func ExpandDeploymentSettingCluster(input []ClusterModel) *deploymentsettings.De } } -func FlattenDeploymentSettingCluster(input *deploymentsettings.DeploymentCluster) []ClusterModel { +func flattenDeploymentSettingCluster(input *deploymentsettings.DeploymentCluster) []ClusterModel { if input == nil { return make([]ClusterModel, 0) } @@ -983,7 +983,7 @@ func FlattenDeploymentSettingCluster(input *deploymentsettings.DeploymentCluster }} } -func ExpandDeploymentSettingHostNetwork(input []HostNetworkModel) *deploymentsettings.HostNetwork { +func expandDeploymentSettingHostNetwork(input []HostNetworkModel) *deploymentsettings.HostNetwork { if len(input) == 0 { return nil } @@ -991,27 +991,27 @@ func ExpandDeploymentSettingHostNetwork(input []HostNetworkModel) *deploymentset v := input[0] return &deploymentsettings.HostNetwork{ - Intents: ExpandDeploymentSettingHostNetworkIntent(v.Intent), + Intents: expandDeploymentSettingHostNetworkIntent(v.Intent), EnableStorageAutoIP: pointer.To(v.StorageAutoIpEnabled), StorageConnectivitySwitchless: pointer.To(v.StorageConnectivitySwitchlessEnabled), - StorageNetworks: ExpandDeploymentSettingHostNetworkStorageNetwork(v.StorageNetwork), + StorageNetworks: expandDeploymentSettingHostNetworkStorageNetwork(v.StorageNetwork), } } -func FlattenDeploymentSettingHostNetwork(input *deploymentsettings.HostNetwork) []HostNetworkModel { +func flattenDeploymentSettingHostNetwork(input *deploymentsettings.HostNetwork) []HostNetworkModel { if input == nil { return make([]HostNetworkModel, 0) } return []HostNetworkModel{{ - Intent: FlattenDeploymentSettingHostNetworkIntent(input.Intents), + Intent: flattenDeploymentSettingHostNetworkIntent(input.Intents), StorageAutoIpEnabled: pointer.From(input.EnableStorageAutoIP), StorageConnectivitySwitchlessEnabled: pointer.From(input.StorageConnectivitySwitchless), - StorageNetwork: FlattenDeploymentSettingHostNetworkStorageNetwork(input.StorageNetworks), + StorageNetwork: flattenDeploymentSettingHostNetworkStorageNetwork(input.StorageNetworks), }} } -func ExpandDeploymentSettingHostNetworkIntent(input []HostNetworkIntentModel) *[]deploymentsettings.Intents { +func expandDeploymentSettingHostNetworkIntent(input []HostNetworkIntentModel) *[]deploymentsettings.Intents { if len(input) == 0 { return nil } @@ -1020,21 +1020,21 @@ func ExpandDeploymentSettingHostNetworkIntent(input []HostNetworkIntentModel) *[ for _, item := range input { results = append(results, deploymentsettings.Intents{ Adapter: pointer.To(item.Adapter), - AdapterPropertyOverrides: ExpandHostNetworkIntentAdapterPropertyOverride(item.AdapterPropertyOverride), + AdapterPropertyOverrides: expandHostNetworkIntentAdapterPropertyOverride(item.AdapterPropertyOverride), Name: pointer.To(item.Name), OverrideAdapterProperty: pointer.To(item.AdapterPropertyOverrideEnabled), OverrideQosPolicy: pointer.To(item.QosPolicyOverrideEnabled), OverrideVirtualSwitchConfiguration: pointer.To(item.VirtualSwitchConfigurationOverrideEnabled), - QosPolicyOverrides: ExpandHostNetworkIntentQosPolicyOverride(item.QosPolicyOverride), + QosPolicyOverrides: expandHostNetworkIntentQosPolicyOverride(item.QosPolicyOverride), TrafficType: pointer.To(item.TrafficType), - VirtualSwitchConfigurationOverrides: ExpandHostNetworkIntentVirtualSwitchConfigurationOverride(item.VirtualSwitchConfigurationOverride), + VirtualSwitchConfigurationOverrides: expandHostNetworkIntentVirtualSwitchConfigurationOverride(item.VirtualSwitchConfigurationOverride), }) } return &results } -func FlattenDeploymentSettingHostNetworkIntent(input *[]deploymentsettings.Intents) []HostNetworkIntentModel { +func flattenDeploymentSettingHostNetworkIntent(input *[]deploymentsettings.Intents) []HostNetworkIntentModel { if input == nil { return make([]HostNetworkIntentModel, 0) } @@ -1043,21 +1043,21 @@ func FlattenDeploymentSettingHostNetworkIntent(input *[]deploymentsettings.Inten for _, item := range *input { results = append(results, HostNetworkIntentModel{ Adapter: pointer.From(item.Adapter), - AdapterPropertyOverride: FlattenHostNetworkIntentAdapterPropertyOverride(item.AdapterPropertyOverrides), + AdapterPropertyOverride: flattenHostNetworkIntentAdapterPropertyOverride(item.AdapterPropertyOverrides), Name: pointer.From(item.Name), AdapterPropertyOverrideEnabled: pointer.From(item.OverrideAdapterProperty), QosPolicyOverrideEnabled: pointer.From(item.OverrideQosPolicy), VirtualSwitchConfigurationOverrideEnabled: pointer.From(item.OverrideVirtualSwitchConfiguration), - QosPolicyOverride: FlattenHostNetworkIntentQosPolicyOverride(item.QosPolicyOverrides), + QosPolicyOverride: flattenHostNetworkIntentQosPolicyOverride(item.QosPolicyOverrides), TrafficType: pointer.From(item.TrafficType), - VirtualSwitchConfigurationOverride: FlattenHostNetworkIntentVirtualSwitchConfigurationOverride(item.VirtualSwitchConfigurationOverrides), + VirtualSwitchConfigurationOverride: flattenHostNetworkIntentVirtualSwitchConfigurationOverride(item.VirtualSwitchConfigurationOverrides), }) } return results } -func ExpandHostNetworkIntentAdapterPropertyOverride(input []HostNetworkIntentAdapterPropertyOverrideModel) *deploymentsettings.AdapterPropertyOverrides { +func expandHostNetworkIntentAdapterPropertyOverride(input []HostNetworkIntentAdapterPropertyOverrideModel) *deploymentsettings.AdapterPropertyOverrides { if len(input) == 0 { return &deploymentsettings.AdapterPropertyOverrides{ JumboPacket: pointer.To(""), @@ -1075,7 +1075,7 @@ func ExpandHostNetworkIntentAdapterPropertyOverride(input []HostNetworkIntentAda } } -func FlattenHostNetworkIntentAdapterPropertyOverride(input *deploymentsettings.AdapterPropertyOverrides) []HostNetworkIntentAdapterPropertyOverrideModel { +func flattenHostNetworkIntentAdapterPropertyOverride(input *deploymentsettings.AdapterPropertyOverrides) []HostNetworkIntentAdapterPropertyOverrideModel { if input == nil { return make([]HostNetworkIntentAdapterPropertyOverrideModel, 0) } @@ -1096,7 +1096,7 @@ func FlattenHostNetworkIntentAdapterPropertyOverride(input *deploymentsettings.A }} } -func ExpandHostNetworkIntentQosPolicyOverride(input []HostNetworkIntentQosPolicyOverrideModel) *deploymentsettings.QosPolicyOverrides { +func expandHostNetworkIntentQosPolicyOverride(input []HostNetworkIntentQosPolicyOverrideModel) *deploymentsettings.QosPolicyOverrides { if len(input) == 0 { return &deploymentsettings.QosPolicyOverrides{ BandwidthPercentageSMB: pointer.To(""), @@ -1114,7 +1114,7 @@ func ExpandHostNetworkIntentQosPolicyOverride(input []HostNetworkIntentQosPolicy } } -func FlattenHostNetworkIntentQosPolicyOverride(input *deploymentsettings.QosPolicyOverrides) []HostNetworkIntentQosPolicyOverrideModel { +func flattenHostNetworkIntentQosPolicyOverride(input *deploymentsettings.QosPolicyOverrides) []HostNetworkIntentQosPolicyOverrideModel { if input == nil { return make([]HostNetworkIntentQosPolicyOverrideModel, 0) } @@ -1135,7 +1135,7 @@ func FlattenHostNetworkIntentQosPolicyOverride(input *deploymentsettings.QosPoli }} } -func ExpandHostNetworkIntentVirtualSwitchConfigurationOverride(input []HostNetworkIntentVirtualSwitchConfigurationOverrideModel) *deploymentsettings.VirtualSwitchConfigurationOverrides { +func expandHostNetworkIntentVirtualSwitchConfigurationOverride(input []HostNetworkIntentVirtualSwitchConfigurationOverrideModel) *deploymentsettings.VirtualSwitchConfigurationOverrides { if len(input) == 0 { return &deploymentsettings.VirtualSwitchConfigurationOverrides{ EnableIov: pointer.To(""), @@ -1151,7 +1151,7 @@ func ExpandHostNetworkIntentVirtualSwitchConfigurationOverride(input []HostNetwo } } -func FlattenHostNetworkIntentVirtualSwitchConfigurationOverride(input *deploymentsettings.VirtualSwitchConfigurationOverrides) []HostNetworkIntentVirtualSwitchConfigurationOverrideModel { +func flattenHostNetworkIntentVirtualSwitchConfigurationOverride(input *deploymentsettings.VirtualSwitchConfigurationOverrides) []HostNetworkIntentVirtualSwitchConfigurationOverrideModel { if input == nil { return make([]HostNetworkIntentVirtualSwitchConfigurationOverrideModel, 0) } @@ -1170,7 +1170,7 @@ func FlattenHostNetworkIntentVirtualSwitchConfigurationOverride(input *deploymen }} } -func ExpandDeploymentSettingHostNetworkStorageNetwork(input []HostNetworkStorageNetworkModel) *[]deploymentsettings.StorageNetworks { +func expandDeploymentSettingHostNetworkStorageNetwork(input []HostNetworkStorageNetworkModel) *[]deploymentsettings.StorageNetworks { if len(input) == 0 { return nil } @@ -1187,7 +1187,7 @@ func ExpandDeploymentSettingHostNetworkStorageNetwork(input []HostNetworkStorage return &results } -func FlattenDeploymentSettingHostNetworkStorageNetwork(input *[]deploymentsettings.StorageNetworks) []HostNetworkStorageNetworkModel { +func flattenDeploymentSettingHostNetworkStorageNetwork(input *[]deploymentsettings.StorageNetworks) []HostNetworkStorageNetworkModel { if input == nil { return make([]HostNetworkStorageNetworkModel, 0) } @@ -1204,7 +1204,7 @@ func FlattenDeploymentSettingHostNetworkStorageNetwork(input *[]deploymentsettin return results } -func ExpandDeploymentSettingInfrastructureNetwork(input []InfrastructureNetworkModel) *[]deploymentsettings.InfrastructureNetwork { +func expandDeploymentSettingInfrastructureNetwork(input []InfrastructureNetworkModel) *[]deploymentsettings.InfrastructureNetwork { if len(input) == 0 { return nil } @@ -1214,7 +1214,7 @@ func ExpandDeploymentSettingInfrastructureNetwork(input []InfrastructureNetworkM results = append(results, deploymentsettings.InfrastructureNetwork{ DnsServers: pointer.To(item.DnsServer), Gateway: pointer.To(item.Gateway), - IPPools: ExpandDeploymentSettingInfrastructureNetworkIpPool(item.IpPool), + IPPools: expandDeploymentSettingInfrastructureNetworkIpPool(item.IpPool), SubnetMask: pointer.To(item.SubnetMask), UseDhcp: pointer.To(item.DhcpEnabled), }) @@ -1223,7 +1223,7 @@ func ExpandDeploymentSettingInfrastructureNetwork(input []InfrastructureNetworkM return &results } -func FlattenDeploymentSettingInfrastructureNetwork(input *[]deploymentsettings.InfrastructureNetwork) []InfrastructureNetworkModel { +func flattenDeploymentSettingInfrastructureNetwork(input *[]deploymentsettings.InfrastructureNetwork) []InfrastructureNetworkModel { if input == nil { return make([]InfrastructureNetworkModel, 0) } @@ -1234,7 +1234,7 @@ func FlattenDeploymentSettingInfrastructureNetwork(input *[]deploymentsettings.I DhcpEnabled: pointer.From(item.UseDhcp), DnsServer: pointer.From(item.DnsServers), Gateway: pointer.From(item.Gateway), - IpPool: FlattenDeploymentSettingInfrastructureNetworkIpPool(item.IPPools), + IpPool: flattenDeploymentSettingInfrastructureNetworkIpPool(item.IPPools), SubnetMask: pointer.From(item.SubnetMask), }) } @@ -1242,7 +1242,7 @@ func FlattenDeploymentSettingInfrastructureNetwork(input *[]deploymentsettings.I return results } -func ExpandDeploymentSettingInfrastructureNetworkIpPool(input []IpPoolModel) *[]deploymentsettings.IPPools { +func expandDeploymentSettingInfrastructureNetworkIpPool(input []IpPoolModel) *[]deploymentsettings.IPPools { if len(input) == 0 { return nil } @@ -1258,7 +1258,7 @@ func ExpandDeploymentSettingInfrastructureNetworkIpPool(input []IpPoolModel) *[] return &results } -func FlattenDeploymentSettingInfrastructureNetworkIpPool(input *[]deploymentsettings.IPPools) []IpPoolModel { +func flattenDeploymentSettingInfrastructureNetworkIpPool(input *[]deploymentsettings.IPPools) []IpPoolModel { if input == nil { return make([]IpPoolModel, 0) } @@ -1274,7 +1274,7 @@ func FlattenDeploymentSettingInfrastructureNetworkIpPool(input *[]deploymentsett return results } -func ExpandDeploymentSettingObservability(input ScaleUnitModel) *deploymentsettings.Observability { +func expandDeploymentSettingObservability(input ScaleUnitModel) *deploymentsettings.Observability { return &deploymentsettings.Observability{ EpisodicDataUpload: pointer.To(input.EpisodicDataUploadEnabled), EuLocation: pointer.To(input.EuLocationEnabled), @@ -1282,7 +1282,7 @@ func ExpandDeploymentSettingObservability(input ScaleUnitModel) *deploymentsetti } } -func ExpandDeploymentSettingOptionalService(input []OptionalServiceModel) *deploymentsettings.OptionalServices { +func expandDeploymentSettingOptionalService(input []OptionalServiceModel) *deploymentsettings.OptionalServices { if len(input) == 0 { return nil } @@ -1294,7 +1294,7 @@ func ExpandDeploymentSettingOptionalService(input []OptionalServiceModel) *deplo } } -func FlattenDeploymentSettingOptionalService(input *deploymentsettings.OptionalServices) []OptionalServiceModel { +func flattenDeploymentSettingOptionalService(input *deploymentsettings.OptionalServices) []OptionalServiceModel { if input == nil { return make([]OptionalServiceModel, 0) } @@ -1304,7 +1304,7 @@ func FlattenDeploymentSettingOptionalService(input *deploymentsettings.OptionalS }} } -func ExpandDeploymentSettingPhysicalNode(input []PhysicalNodeModel) *[]deploymentsettings.PhysicalNodes { +func expandDeploymentSettingPhysicalNode(input []PhysicalNodeModel) *[]deploymentsettings.PhysicalNodes { if len(input) == 0 { return nil } @@ -1320,7 +1320,7 @@ func ExpandDeploymentSettingPhysicalNode(input []PhysicalNodeModel) *[]deploymen return &results } -func FlattenDeploymentSettingPhysicalNode(input *[]deploymentsettings.PhysicalNodes) []PhysicalNodeModel { +func flattenDeploymentSettingPhysicalNode(input *[]deploymentsettings.PhysicalNodes) []PhysicalNodeModel { if input == nil { return make([]PhysicalNodeModel, 0) } @@ -1336,7 +1336,7 @@ func FlattenDeploymentSettingPhysicalNode(input *[]deploymentsettings.PhysicalNo return results } -func ExpandDeploymentSettingSecuritySetting(input ScaleUnitModel) *deploymentsettings.DeploymentSecuritySettings { +func expandDeploymentSettingSecuritySetting(input ScaleUnitModel) *deploymentsettings.DeploymentSecuritySettings { return &deploymentsettings.DeploymentSecuritySettings{ BitlockerBootVolume: pointer.To(input.BitlockerBootVolumeEnabled), BitlockerDataVolumes: pointer.To(input.BitlockerDataVolumeEnabled), @@ -1351,7 +1351,7 @@ func ExpandDeploymentSettingSecuritySetting(input ScaleUnitModel) *deploymentset } } -func ExpandDeploymentSettingStorage(input []StorageModel) *deploymentsettings.Storage { +func expandDeploymentSettingStorage(input []StorageModel) *deploymentsettings.Storage { if len(input) == 0 { return nil } @@ -1363,7 +1363,7 @@ func ExpandDeploymentSettingStorage(input []StorageModel) *deploymentsettings.St } } -func FlattenDeploymentSettingStorage(input *deploymentsettings.Storage) []StorageModel { +func flattenDeploymentSettingStorage(input *deploymentsettings.Storage) []StorageModel { if input == nil { return make([]StorageModel, 0) } From c51ceaf4ec966ae30dec04c78a91e007bf3235c7 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Thu, 4 Jul 2024 06:20:28 +0000 Subject: [PATCH 11/16] setID before create --- .../stack_hci_deployment_setting_resource.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go index cfbbf6221fdb..9f2a3226a9e1 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go @@ -733,18 +733,12 @@ func (r StackHCIDeploymentSettingResource) Create() sdk.ResourceFunc { }, } - // do validation - future, err := client.CreateOrUpdate(ctx, id, payload) - if err != nil { - return fmt.Errorf("validate %s: %+v", id, err) - } - // the resource may exist even validation error metadata.SetID(id) - // poll validation - if err := future.Poller.PollUntilDone(ctx); err != nil { - return fmt.Errorf("polling after validate %s: %+v", id, err) + // do validation + if err := client.CreateOrUpdateThenPoll(ctx, id, payload); err != nil { + return fmt.Errorf("validating %s: %+v", id, err) } // do deployment From 67846331541e833ad425987403e272f2ef11afdb Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Thu, 1 Aug 2024 07:35:58 +0000 Subject: [PATCH 12/16] fix test exist func --- .../stack_hci_deployment_setting_resource_test.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go index 6628472a61fa..45c6952ba497 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go @@ -6,13 +6,12 @@ import ( "os" "testing" - "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/deploymentsettings" "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/tf/pluginsdk" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) type StackHCIDeploymentSettingResource struct{} @@ -95,14 +94,10 @@ func (r StackHCIDeploymentSettingResource) Exists(ctx context.Context, client *c resp, err := clusterClient.Get(ctx, *id) if err != nil { - if response.WasNotFound(resp.HttpResponse) { - return utils.Bool(false), nil - } - return nil, fmt.Errorf("retrieving %s: %+v", *id, err) } - return utils.Bool(resp.Model != nil), nil + return pointer.To(resp.Model != nil), nil } func (r StackHCIDeploymentSettingResource) basic(data acceptance.TestData) string { From 2a8949c409683b644ea993a85c555766d2334b3f Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Mon, 5 Aug 2024 05:24:37 +0000 Subject: [PATCH 13/16] remove feature toggle --- internal/features/defaults.go | 4 - internal/features/user_flags.go | 6 -- internal/provider/features.go | 33 -------- internal/provider/features_test.go | 24 ------ .../stack_hci_deployment_setting_resource.go | 78 +++++++++---------- ...ck_hci_deployment_setting_resource_test.go | 10 +-- .../docs/guides/features-block.html.markdown | 13 ---- ...stack_hci_deployment_setting.html.markdown | 11 --- 8 files changed, 39 insertions(+), 140 deletions(-) diff --git a/internal/features/defaults.go b/internal/features/defaults.go index c125cc0fa710..12c24186eb3c 100644 --- a/internal/features/defaults.go +++ b/internal/features/defaults.go @@ -17,10 +17,6 @@ func Default() UserFeatures { ApplicationInsights: ApplicationInsightFeatures{ DisableGeneratedRule: false, }, - AzureStackHci: AzureStackHciFeatures{ - DeleteArcBridgeOnDestroy: false, - DeleteCustomLocationOnDestroy: false, - }, CognitiveAccount: CognitiveAccountFeatures{ PurgeSoftDeleteOnDestroy: true, }, diff --git a/internal/features/user_flags.go b/internal/features/user_flags.go index 6d2367d063e4..4b367743ae89 100644 --- a/internal/features/user_flags.go +++ b/internal/features/user_flags.go @@ -7,7 +7,6 @@ type UserFeatures struct { ApiManagement ApiManagementFeatures AppConfiguration AppConfigurationFeatures ApplicationInsights ApplicationInsightFeatures - AzureStackHci AzureStackHciFeatures CognitiveAccount CognitiveAccountFeatures VirtualMachine VirtualMachineFeatures VirtualMachineScaleSet VirtualMachineScaleSetFeatures @@ -76,11 +75,6 @@ type ApplicationInsightFeatures struct { DisableGeneratedRule bool } -type AzureStackHciFeatures struct { - DeleteArcBridgeOnDestroy bool - DeleteCustomLocationOnDestroy bool -} - type ManagedDiskFeatures struct { ExpandWithoutDowntime bool } diff --git a/internal/provider/features.go b/internal/provider/features.go index 6863e9c68738..38ef20645d16 100644 --- a/internal/provider/features.go +++ b/internal/provider/features.go @@ -73,26 +73,6 @@ func schemaFeatures(supportLegacyTestSuite bool) *pluginsdk.Schema { }, }, - "azure_stack_hci": { - Type: pluginsdk.TypeList, - Optional: true, - MaxItems: 1, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "delete_arc_bridge_on_destroy": { - Type: pluginsdk.TypeBool, - Optional: true, - Default: false, - }, - "delete_custom_location_on_destroy": { - Type: pluginsdk.TypeBool, - Optional: true, - Default: false, - }, - }, - }, - }, - "cognitive_account": { Type: pluginsdk.TypeList, Optional: true, @@ -463,19 +443,6 @@ func expandFeatures(input []interface{}) features.UserFeatures { } } - if raw, ok := val["azure_stack_hci"]; ok { - items := raw.([]interface{}) - if len(items) > 0 && items[0] != nil { - azureStackHciRaw := items[0].(map[string]interface{}) - if v, ok := azureStackHciRaw["delete_arc_bridge_on_destroy"]; ok { - featuresMap.AzureStackHci.DeleteArcBridgeOnDestroy = v.(bool) - } - if v, ok := azureStackHciRaw["delete_custom_location_on_destroy"]; ok { - featuresMap.AzureStackHci.DeleteCustomLocationOnDestroy = v.(bool) - } - } - } - if raw, ok := val["cognitive_account"]; ok { items := raw.([]interface{}) if len(items) > 0 && items[0] != nil { diff --git a/internal/provider/features_test.go b/internal/provider/features_test.go index b40395826234..44414fd56278 100644 --- a/internal/provider/features_test.go +++ b/internal/provider/features_test.go @@ -32,10 +32,6 @@ func TestExpandFeatures(t *testing.T) { ApplicationInsights: features.ApplicationInsightFeatures{ DisableGeneratedRule: false, }, - AzureStackHci: features.AzureStackHciFeatures{ - DeleteArcBridgeOnDestroy: false, - DeleteCustomLocationOnDestroy: false, - }, CognitiveAccount: features.CognitiveAccountFeatures{ PurgeSoftDeleteOnDestroy: true, }, @@ -115,12 +111,6 @@ func TestExpandFeatures(t *testing.T) { "disable_generated_rule": true, }, }, - "azure_stack_hci": []interface{}{ - map[string]interface{}{ - "delete_arc_bridge_on_destroy": true, - "delete_custom_location_on_destroy": true, - }, - }, "cognitive_account": []interface{}{ map[string]interface{}{ "purge_soft_delete_on_destroy": true, @@ -217,10 +207,6 @@ func TestExpandFeatures(t *testing.T) { ApplicationInsights: features.ApplicationInsightFeatures{ DisableGeneratedRule: true, }, - AzureStackHci: features.AzureStackHciFeatures{ - DeleteArcBridgeOnDestroy: true, - DeleteCustomLocationOnDestroy: true, - }, CognitiveAccount: features.CognitiveAccountFeatures{ PurgeSoftDeleteOnDestroy: true, }, @@ -300,12 +286,6 @@ func TestExpandFeatures(t *testing.T) { "disable_generated_rule": false, }, }, - "azure_stack_hci": []interface{}{ - map[string]interface{}{ - "delete_arc_bridge_on_destroy": false, - "delete_custom_location_on_destroy": false, - }, - }, "cognitive_account": []interface{}{ map[string]interface{}{ "purge_soft_delete_on_destroy": false, @@ -402,10 +382,6 @@ func TestExpandFeatures(t *testing.T) { ApplicationInsights: features.ApplicationInsightFeatures{ DisableGeneratedRule: false, }, - AzureStackHci: features.AzureStackHciFeatures{ - DeleteArcBridgeOnDestroy: false, - DeleteCustomLocationOnDestroy: false, - }, CognitiveAccount: features.CognitiveAccountFeatures{ PurgeSoftDeleteOnDestroy: false, }, diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go index 9f2a3226a9e1..c70679837434 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go @@ -813,60 +813,56 @@ func (StackHCIDeploymentSettingResource) Delete() sdk.ResourceFunc { return fmt.Errorf("deleting %s: %+v", id, err) } - if metadata.Client.Features.AzureStackHci.DeleteArcBridgeOnDestroy && !response.WasNotFound(resp.HttpResponse) { - applianceName := fmt.Sprintf("%s-arcbridge", id.ClusterName) - applianceId := appliances.NewApplianceID(id.SubscriptionId, id.ResourceGroupName, applianceName) + applianceName := fmt.Sprintf("%s-arcbridge", id.ClusterName) + applianceId := appliances.NewApplianceID(id.SubscriptionId, id.ResourceGroupName, applianceName) - log.Printf("[DEBUG] Deleting Arc Resource Bridge Appliance %s generated during deployment", applianceId.ID()) + log.Printf("[DEBUG] Deleting Arc Resource Bridge Appliance %s generated during deployment", applianceId.ID()) - applianceClient := metadata.Client.ArcResourceBridge.AppliancesClient - if err := applianceClient.DeleteThenPoll(ctx, applianceId); err != nil { - return fmt.Errorf("deleting %s: %+v", applianceId, err) - } + applianceClient := metadata.Client.ArcResourceBridge.AppliancesClient + if err := applianceClient.DeleteThenPoll(ctx, applianceId); err != nil { + return fmt.Errorf("deleting %s: %+v", applianceId, err) } - if metadata.Client.Features.AzureStackHci.DeleteCustomLocationOnDestroy && !response.WasNotFound(resp.HttpResponse) { - log.Printf("[DEBUG] Deleting Custom Location and Storage Containers generated during deployment") + log.Printf("[DEBUG] Deleting Custom Location and Storage Containers generated during deployment") - var customLocationName string - if resp.Model != nil && resp.Model.Properties != nil && - len(resp.Model.Properties.DeploymentConfiguration.ScaleUnits) > 0 && - resp.Model.Properties.DeploymentConfiguration.ScaleUnits[0].DeploymentData.OptionalServices != nil { - customLocationName = pointer.From(resp.Model.Properties.DeploymentConfiguration.ScaleUnits[0].DeploymentData.OptionalServices.CustomLocation) - } + var customLocationName string + if resp.Model != nil && resp.Model.Properties != nil && + len(resp.Model.Properties.DeploymentConfiguration.ScaleUnits) > 0 && + resp.Model.Properties.DeploymentConfiguration.ScaleUnits[0].DeploymentData.OptionalServices != nil { + customLocationName = pointer.From(resp.Model.Properties.DeploymentConfiguration.ScaleUnits[0].DeploymentData.OptionalServices.CustomLocation) + } - if customLocationName != "" { - customLocationId := customlocations.NewCustomLocationID(id.SubscriptionId, id.ResourceGroupName, customLocationName) + if customLocationName != "" { + customLocationId := customlocations.NewCustomLocationID(id.SubscriptionId, id.ResourceGroupName, customLocationName) - log.Printf("[DEBUG] Deleting Storage Containers under Custom Location %s", customLocationId.ID()) + log.Printf("[DEBUG] Deleting Storage Containers under Custom Location %s", customLocationId.ID()) - storageContainerClient := metadata.Client.AzureStackHCI.StorageContainers - resourceGroupId := commonids.NewResourceGroupID(id.SubscriptionId, id.ResourceGroupName) - storageContainers, err := storageContainerClient.ListComplete(ctx, resourceGroupId) - if err != nil { - return fmt.Errorf("retrieving Stack HCI Storage Containers under %s: %+v", resourceGroupId.ID(), err) - } + storageContainerClient := metadata.Client.AzureStackHCI.StorageContainers + resourceGroupId := commonids.NewResourceGroupID(id.SubscriptionId, id.ResourceGroupName) + storageContainers, err := storageContainerClient.ListComplete(ctx, resourceGroupId) + if err != nil { + return fmt.Errorf("retrieving Stack HCI Storage Containers under %s: %+v", resourceGroupId.ID(), err) + } - // match Storage Containers under the Custom Location - storageContainerNamePattern := regexp.MustCompile(`UserStorage[0-9]+-[a-z0-9]{32}`) - for _, v := range storageContainers.Items { - if v.Id != nil && v.ExtendedLocation != nil && v.ExtendedLocation.Name != nil && strings.EqualFold(*v.ExtendedLocation.Name, customLocationId.ID()) && v.Name != nil && storageContainerNamePattern.Match([]byte(*v.Name)) { - storageContainerId, err := storagecontainers.ParseStorageContainerIDInsensitively(*v.Id) - if err != nil { - return err - } - - if err := storageContainerClient.DeleteThenPoll(ctx, *storageContainerId); err != nil { - return err - } + // match Storage Containers under the Custom Location + storageContainerNamePattern := regexp.MustCompile(`UserStorage[0-9]+-[a-z0-9]{32}`) + for _, v := range storageContainers.Items { + if v.Id != nil && v.ExtendedLocation != nil && v.ExtendedLocation.Name != nil && strings.EqualFold(*v.ExtendedLocation.Name, customLocationId.ID()) && v.Name != nil && storageContainerNamePattern.Match([]byte(*v.Name)) { + storageContainerId, err := storagecontainers.ParseStorageContainerIDInsensitively(*v.Id) + if err != nil { + return err } - } - customLocationsClient := metadata.Client.ExtendedLocation.CustomLocations - if err := customLocationsClient.DeleteThenPoll(ctx, customLocationId); err != nil { - return fmt.Errorf("deleting %s: %+v", customLocationId, err) + if err := storageContainerClient.DeleteThenPoll(ctx, *storageContainerId); err != nil { + return err + } } } + + customLocationsClient := metadata.Client.ExtendedLocation.CustomLocations + if err := customLocationsClient.DeleteThenPoll(ctx, customLocationId); err != nil { + return fmt.Errorf("deleting %s: %+v", customLocationId, err) + } } return nil diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go index 45c6952ba497..1e9396782b80 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go @@ -108,12 +108,9 @@ func (r StackHCIDeploymentSettingResource) basic(data acceptance.TestData) strin provider "azurerm" { features { resource_group { + // need to delete arc vm prevent_deletion_if_contains_resources = false } - azure_stack_hci { - delete_arc_bridge_on_destroy = true - delete_custom_location_on_destroy = true - } } } @@ -343,12 +340,9 @@ func (r StackHCIDeploymentSettingResource) complete(data acceptance.TestData) st provider "azurerm" { features { resource_group { + // need to delete arc vm prevent_deletion_if_contains_resources = false } - azure_stack_hci { - delete_arc_bridge_on_destroy = true - delete_custom_location_on_destroy = true - } } } diff --git a/website/docs/guides/features-block.html.markdown b/website/docs/guides/features-block.html.markdown index 158778a78b52..1c9c8eac9e64 100644 --- a/website/docs/guides/features-block.html.markdown +++ b/website/docs/guides/features-block.html.markdown @@ -41,11 +41,6 @@ provider "azurerm" { disable_generated_rule = false } - azure_stack_hci { - delete_arc_bridge_on_destroy = false - delete_custom_location_on_destroy = false - } - cognitive_account { purge_soft_delete_on_destroy = true } @@ -166,14 +161,6 @@ The `application_insights` block supports the following: --- -The `azure_stack_hci` block supports the following: - -* `delete_arc_bridge_on_destroy` - (Optional) Should the `azurerm_stack_hci_deployment_setting` resources delete the Arc Resource Bridge during the destroy step? Defaults to `false`. - -* `delete_custom_location_on_destroy` - (Optional) Should the `azurerm_stack_hci_deployment_setting` resources delete the Custom Location and the Azure Stack HCI Storage Containers in the location during the destroy step? Defaults to `false`. - ---- - The `cognitive_account` block supports the following: * `purge_soft_delete_on_destroy` - (Optional) Should the `azurerm_cognitive_account` resources be permanently deleted (e.g. purged) when destroyed? Defaults to `true`. diff --git a/website/docs/r/stack_hci_deployment_setting.html.markdown b/website/docs/r/stack_hci_deployment_setting.html.markdown index 003fedccf885..5f7ab47f04d0 100644 --- a/website/docs/r/stack_hci_deployment_setting.html.markdown +++ b/website/docs/r/stack_hci_deployment_setting.html.markdown @@ -12,22 +12,11 @@ Manages a Stack HCI Deployment Setting. -> Note: Completion of the prerequisites of deploying the Azure Stack HCI in your environment is outside the scope of this document. For more details refer to the [Azure Stack HCI deployment sequence](https://learn.microsoft.com/en-us/azure-stack/hci/deploy/deployment-introduction#deployment-sequence). If you encounter issues completing the prerequisites, we'd recommend opening a ticket with Microsoft Support. --> Note: The Azure Provider include a Feature Toggle `delete_arc_bridge_on_destroy` which controls whether to delete Arc Resource Bridge generated on destroy, and Feature Toggle `delete_custom_location_on_destroy` which controls whether to delete Custom Location on destroy. The Provider will not do the deletion by default. See [the Features block documentation](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/features-block) for more information on Feature Toggles within Terraform. - ## Example Usage ```hcl provider "azuread" {} -provider "azurerm" { - features { - azure_stack_hci { - delete_arc_bridge_on_destroy = false - delete_custom_location_on_destroy = false - } - } -} - variable "local_admin_user" { description = "The username of the local administrator account." sensitive = true From c0fa7eb30a9cef73e11b40f92589a951b303d595 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Thu, 8 Aug 2024 04:45:16 +0000 Subject: [PATCH 14/16] add notes on additional resources --- .../stack_hci_deployment_setting_resource.go | 19 ++++++++++--------- ...stack_hci_deployment_setting.html.markdown | 2 ++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go index c70679837434..1e9ad8d693ae 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go @@ -816,14 +816,14 @@ func (StackHCIDeploymentSettingResource) Delete() sdk.ResourceFunc { applianceName := fmt.Sprintf("%s-arcbridge", id.ClusterName) applianceId := appliances.NewApplianceID(id.SubscriptionId, id.ResourceGroupName, applianceName) - log.Printf("[DEBUG] Deleting Arc Resource Bridge Appliance %s generated during deployment", applianceId.ID()) + log.Printf("[DEBUG] Deleting Arc Resource Bridge Appliance generated during deployment: %s", applianceId.ID()) applianceClient := metadata.Client.ArcResourceBridge.AppliancesClient if err := applianceClient.DeleteThenPoll(ctx, applianceId); err != nil { - return fmt.Errorf("deleting %s: %+v", applianceId, err) + return fmt.Errorf("deleting Arc Resource Bridge Appliance generated during deployment: deleting %s: %+v", applianceId, err) } - log.Printf("[DEBUG] Deleting Custom Location and Storage Containers generated during deployment") + log.Printf("[DEBUG] Deleting Custom Location and Stack HCI Storage Paths generated during deployment") var customLocationName string if resp.Model != nil && resp.Model.Properties != nil && @@ -835,33 +835,34 @@ func (StackHCIDeploymentSettingResource) Delete() sdk.ResourceFunc { if customLocationName != "" { customLocationId := customlocations.NewCustomLocationID(id.SubscriptionId, id.ResourceGroupName, customLocationName) - log.Printf("[DEBUG] Deleting Storage Containers under Custom Location %s", customLocationId.ID()) + // we need to delete the Storage Paths before the Custom Location, otherwise the Custom Location cannot be deleted if there is any Resource in it + log.Printf("[DEBUG] Deleting Stack HCI Storage Paths under Custom Location %s", customLocationId.ID()) storageContainerClient := metadata.Client.AzureStackHCI.StorageContainers resourceGroupId := commonids.NewResourceGroupID(id.SubscriptionId, id.ResourceGroupName) storageContainers, err := storageContainerClient.ListComplete(ctx, resourceGroupId) if err != nil { - return fmt.Errorf("retrieving Stack HCI Storage Containers under %s: %+v", resourceGroupId.ID(), err) + return fmt.Errorf("deleting Stack HCI Storage Paths generated during deployment: retrieving Storage Path under %s: %+v", resourceGroupId, err) } - // match Storage Containers under the Custom Location + // match Storage Paths under the Custom Location, the generated Storage Path name should match below pattern storageContainerNamePattern := regexp.MustCompile(`UserStorage[0-9]+-[a-z0-9]{32}`) for _, v := range storageContainers.Items { if v.Id != nil && v.ExtendedLocation != nil && v.ExtendedLocation.Name != nil && strings.EqualFold(*v.ExtendedLocation.Name, customLocationId.ID()) && v.Name != nil && storageContainerNamePattern.Match([]byte(*v.Name)) { storageContainerId, err := storagecontainers.ParseStorageContainerIDInsensitively(*v.Id) if err != nil { - return err + return fmt.Errorf("parsing the Stack HCI Storage Path ID generated during deployment: %+v", err) } if err := storageContainerClient.DeleteThenPoll(ctx, *storageContainerId); err != nil { - return err + return fmt.Errorf("deleting the Stack HCI Storage Path generated during deployment: deleting %s: %+v", storageContainerId, err) } } } customLocationsClient := metadata.Client.ExtendedLocation.CustomLocations if err := customLocationsClient.DeleteThenPoll(ctx, customLocationId); err != nil { - return fmt.Errorf("deleting %s: %+v", customLocationId, err) + return fmt.Errorf("deleting the Custom Location generated during deployment: deleting %s: %+v", customLocationId, err) } } diff --git a/website/docs/r/stack_hci_deployment_setting.html.markdown b/website/docs/r/stack_hci_deployment_setting.html.markdown index 5f7ab47f04d0..d8af9fa1dd60 100644 --- a/website/docs/r/stack_hci_deployment_setting.html.markdown +++ b/website/docs/r/stack_hci_deployment_setting.html.markdown @@ -12,6 +12,8 @@ Manages a Stack HCI Deployment Setting. -> Note: Completion of the prerequisites of deploying the Azure Stack HCI in your environment is outside the scope of this document. For more details refer to the [Azure Stack HCI deployment sequence](https://learn.microsoft.com/en-us/azure-stack/hci/deploy/deployment-introduction#deployment-sequence). If you encounter issues completing the prerequisites, we'd recommend opening a ticket with Microsoft Support. +-> Note: During the deployment process, the service will generate additional resources, including a new Arc Bridge Appliance and a Custom Location containing several Stack HCI Storage Paths. The provider will attempt to remove these resources on the deletion or recreation of `azurerm_stack_hci_deployment_setting`. + ## Example Usage ```hcl From 80c3c3fbace6d7f09e4269c75896568eaa43ac63 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Sat, 14 Sep 2024 10:06:35 +0000 Subject: [PATCH 15/16] add more validations --- .../stack_hci_deployment_setting_resource.go | 74 ++++++++++--------- ...ck_hci_deployment_setting_resource_test.go | 62 ++++++++-------- ...stack_hci_deployment_setting.html.markdown | 10 +-- 3 files changed, 76 insertions(+), 70 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go index 2b4e76e48292..a13e42057ab5 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go @@ -18,6 +18,7 @@ import ( "github.com/hashicorp/go-azure-sdk/resource-manager/hybridcompute/2022-11-10/machines" "github.com/hashicorp/go-azure-sdk/resource-manager/resourceconnector/2022-10-27/appliances" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + storageValidate "github.com/hashicorp/terraform-provider-azurerm/internal/storage/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" ) @@ -42,16 +43,16 @@ type StackHCIDeploymentSettingModel struct { } type ScaleUnitModel struct { - AdouPath string `tfschema:"adou_path"` - Cluster []ClusterModel `tfschema:"cluster"` - DomainFqdn string `tfschema:"domain_fqdn"` - HostNetwork []HostNetworkModel `tfschema:"host_network"` - InfrastructureNetwork []InfrastructureNetworkModel `tfschema:"infrastructure_network"` - NamePrefix string `tfschema:"name_prefix"` - OptionalService []OptionalServiceModel `tfschema:"optional_service"` - PhysicalNode []PhysicalNodeModel `tfschema:"physical_node"` - SecretsLocation string `tfschema:"secrets_location"` - Storage []StorageModel `tfschema:"storage"` + ActiveDirectoryOrganizationalUnitPath string `tfschema:"active_directory_organizational_unit_path"` + Cluster []ClusterModel `tfschema:"cluster"` + DomainFqdn string `tfschema:"domain_fqdn"` + HostNetwork []HostNetworkModel `tfschema:"host_network"` + InfrastructureNetwork []InfrastructureNetworkModel `tfschema:"infrastructure_network"` + NamePrefix string `tfschema:"name_prefix"` + OptionalService []OptionalServiceModel `tfschema:"optional_service"` + PhysicalNode []PhysicalNodeModel `tfschema:"physical_node"` + SecretsLocation string `tfschema:"secrets_location"` + Storage []StorageModel `tfschema:"storage"` // flatten 'observability' block, for API always return them StreamingDataClientEnabled bool `tfschema:"streaming_data_client_enabled"` @@ -182,7 +183,7 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem MinItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ - "adou_path": { + "active_directory_organizational_unit_path": { Type: pluginsdk.TypeString, Required: true, ForceNew: true, @@ -207,17 +208,20 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem }, "azure_service_endpoint": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringMatch( + regex.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`), + "`azure_service_endpoint` must be a valid domain name, for example, \"core.windows.net\"", + ), }, "cloud_account_name": { Type: pluginsdk.TypeString, Required: true, ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, + ValidateFunc: storageValidate.StorageAccountName, }, "witness_type": { @@ -241,10 +245,13 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem }, "domain_fqdn": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringMatch( + regex.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`), + "`domain_fqdn` must be a valid domain name, for example, \"jumpstart.local\"", + ), }, "host_network": { @@ -733,9 +740,6 @@ func (r StackHCIDeploymentSettingResource) Create() sdk.ResourceFunc { }, } - // the resource may exist even validation error - metadata.SetID(id) - // do validation if err := client.CreateOrUpdateThenPoll(ctx, id, payload); err != nil { return fmt.Errorf("validating %s: %+v", id, err) @@ -747,6 +751,8 @@ func (r StackHCIDeploymentSettingResource) Create() sdk.ResourceFunc { return fmt.Errorf("deploying %s: %+v", id, err) } + metadata.SetID(id) + return nil }, } @@ -880,7 +886,7 @@ func expandDeploymentSettingScaleUnits(input []ScaleUnitModel) []deploymentsetti for _, item := range input { results = append(results, deploymentsettings.ScaleUnits{ DeploymentData: deploymentsettings.DeploymentData{ - AdouPath: pointer.To(item.AdouPath), + AdouPath: pointer.To(item.ActiveDirectoryOrganizationalUnitPath), Cluster: expandDeploymentSettingCluster(item.Cluster), DomainFqdn: pointer.To(item.DomainFqdn), HostNetwork: expandDeploymentSettingHostNetwork(item.HostNetwork), @@ -907,16 +913,16 @@ func flattenDeploymentSettingScaleUnits(input []deploymentsettings.ScaleUnits) [ results := make([]ScaleUnitModel, 0, len(input)) for _, item := range input { result := ScaleUnitModel{ - AdouPath: pointer.From(item.DeploymentData.AdouPath), - Cluster: flattenDeploymentSettingCluster(item.DeploymentData.Cluster), - DomainFqdn: pointer.From(item.DeploymentData.DomainFqdn), - HostNetwork: flattenDeploymentSettingHostNetwork(item.DeploymentData.HostNetwork), - InfrastructureNetwork: flattenDeploymentSettingInfrastructureNetwork(item.DeploymentData.InfrastructureNetwork), - NamePrefix: pointer.From(item.DeploymentData.NamingPrefix), - OptionalService: flattenDeploymentSettingOptionalService(item.DeploymentData.OptionalServices), - PhysicalNode: flattenDeploymentSettingPhysicalNode(item.DeploymentData.PhysicalNodes), - SecretsLocation: pointer.From(item.DeploymentData.SecretsLocation), - Storage: flattenDeploymentSettingStorage(item.DeploymentData.Storage), + ActiveDirectoryOrganizationalUnitPath: pointer.From(item.DeploymentData.AdouPath), + Cluster: flattenDeploymentSettingCluster(item.DeploymentData.Cluster), + DomainFqdn: pointer.From(item.DeploymentData.DomainFqdn), + HostNetwork: flattenDeploymentSettingHostNetwork(item.DeploymentData.HostNetwork), + InfrastructureNetwork: flattenDeploymentSettingInfrastructureNetwork(item.DeploymentData.InfrastructureNetwork), + NamePrefix: pointer.From(item.DeploymentData.NamingPrefix), + OptionalService: flattenDeploymentSettingOptionalService(item.DeploymentData.OptionalServices), + PhysicalNode: flattenDeploymentSettingPhysicalNode(item.DeploymentData.PhysicalNodes), + SecretsLocation: pointer.From(item.DeploymentData.SecretsLocation), + Storage: flattenDeploymentSettingStorage(item.DeploymentData.Storage), } if observability := item.DeploymentData.Observability; observability != nil { diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go index 1e9396782b80..50bb2615882f 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource_test.go @@ -120,13 +120,13 @@ resource "azurerm_stack_hci_deployment_setting" "test" { version = "10.0.0.0" scale_unit { - adou_path = "OU=hci${var.random_string},DC=jumpstart,DC=local" - domain_fqdn = "jumpstart.local" - secrets_location = azurerm_key_vault.DeploymentKeyVault.vault_uri - name_prefix = "hci${var.random_string}" - streaming_data_client_enabled = true - eu_location_enabled = false - episodic_data_upload_enabled = true + active_directory_organizational_unit_path = "OU=hci${var.random_string},DC=jumpstart,DC=local" + domain_fqdn = "jumpstart.local" + secrets_location = azurerm_key_vault.DeploymentKeyVault.vault_uri + name_prefix = "hci${var.random_string}" + streaming_data_client_enabled = true + eu_location_enabled = false + episodic_data_upload_enabled = true bitlocker_boot_volume_enabled = true bitlocker_data_volume_enabled = true @@ -238,13 +238,13 @@ resource "azurerm_stack_hci_deployment_setting" "import" { version = azurerm_stack_hci_deployment_setting.test.version scale_unit { - adou_path = azurerm_stack_hci_deployment_setting.test.scale_unit.0.adou_path - domain_fqdn = azurerm_stack_hci_deployment_setting.test.scale_unit.0.domain_fqdn - secrets_location = azurerm_stack_hci_deployment_setting.test.scale_unit.0.secrets_location - name_prefix = azurerm_stack_hci_deployment_setting.test.scale_unit.0.name_prefix - streaming_data_client_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.streaming_data_client_enabled - eu_location_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.eu_location_enabled - episodic_data_upload_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.episodic_data_upload_enabled + active_directory_organizational_unit_path = azurerm_stack_hci_deployment_setting.test.scale_unit.0.active_directory_organizational_unit_path + domain_fqdn = azurerm_stack_hci_deployment_setting.test.scale_unit.0.domain_fqdn + secrets_location = azurerm_stack_hci_deployment_setting.test.scale_unit.0.secrets_location + name_prefix = azurerm_stack_hci_deployment_setting.test.scale_unit.0.name_prefix + streaming_data_client_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.streaming_data_client_enabled + eu_location_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.eu_location_enabled + episodic_data_upload_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.episodic_data_upload_enabled bitlocker_boot_volume_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.bitlocker_boot_volume_enabled bitlocker_data_volume_enabled = azurerm_stack_hci_deployment_setting.test.scale_unit.0.bitlocker_data_volume_enabled @@ -352,23 +352,23 @@ resource "azurerm_stack_hci_deployment_setting" "test" { version = "10.0.0.0" scale_unit { - adou_path = "OU=hci${var.random_string},DC=jumpstart,DC=local" - domain_fqdn = "jumpstart.local" - secrets_location = azurerm_key_vault.DeploymentKeyVault.vault_uri - name_prefix = "hci${var.random_string}" - streaming_data_client_enabled = true - eu_location_enabled = false - episodic_data_upload_enabled = true - bitlocker_boot_volume_enabled = true - bitlocker_data_volume_enabled = true - credential_guard_enabled = true - drift_control_enabled = true - drtm_protection_enabled = true - hvci_protection_enabled = true - side_channel_mitigation_enabled = true - smb_cluster_encryption_enabled = false - smb_signing_enabled = true - wdac_enabled = true + active_directory_organizational_unit_path = "OU=hci${var.random_string},DC=jumpstart,DC=local" + domain_fqdn = "jumpstart.local" + secrets_location = azurerm_key_vault.DeploymentKeyVault.vault_uri + name_prefix = "hci${var.random_string}" + streaming_data_client_enabled = true + eu_location_enabled = false + episodic_data_upload_enabled = true + bitlocker_boot_volume_enabled = true + bitlocker_data_volume_enabled = true + credential_guard_enabled = true + drift_control_enabled = true + drtm_protection_enabled = true + hvci_protection_enabled = true + side_channel_mitigation_enabled = true + smb_cluster_encryption_enabled = false + smb_signing_enabled = true + wdac_enabled = true cluster { diff --git a/website/docs/r/stack_hci_deployment_setting.html.markdown b/website/docs/r/stack_hci_deployment_setting.html.markdown index d8af9fa1dd60..0619a0bb84fb 100644 --- a/website/docs/r/stack_hci_deployment_setting.html.markdown +++ b/website/docs/r/stack_hci_deployment_setting.html.markdown @@ -236,10 +236,10 @@ resource "azurerm_stack_hci_deployment_setting" "example" { version = "10.0.0.0" scale_unit { - adou_path = "OU=hci,DC=jumpstart,DC=local" - domain_fqdn = "jumpstart.local" - secrets_location = azurerm_key_vault.DeploymentKeyVault.vault_uri - name_prefix = "hci" + active_directory_organizational_unit_path = "OU=hci,DC=jumpstart,DC=local" + domain_fqdn = "jumpstart.local" + secrets_location = azurerm_key_vault.DeploymentKeyVault.vault_uri + name_prefix = "hci" cluster { azure_service_endpoint = "core.windows.net" @@ -499,7 +499,7 @@ A `qos_policy_override` block supports the following: A `scale_unit` block supports the following: -* `adou_path` - (Required) Specify the full name of the Active Directory Organizational Unit container object prepared for the deployment, including the domain components. For example:`OU=HCI01,DC=contoso,DC=com`. Changing this forces a new Stack HCI Deployment Setting to be created. +* `active_directory_organizational_unit_path` - (Required) Specify the full name of the Active Directory Organizational Unit container object prepared for the deployment, including the domain components. For example:`OU=HCI01,DC=contoso,DC=com`. Changing this forces a new Stack HCI Deployment Setting to be created. * `cluster` - (Required) A `cluster` block as defined above. Changing this forces a new Stack HCI Deployment Setting to be created. From 3dde8942ff064e1a7004f2648fa3c05d8cf495be Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Sat, 14 Sep 2024 14:07:20 +0000 Subject: [PATCH 16/16] fix build issue --- .../stack_hci_deployment_setting_resource.go | 8 ++++---- internal/services/extendedlocation/client/client.go | 2 +- website/docs/guides/features-block.html.markdown | 2 -- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go index a13e42057ab5..41c0104c25c8 100644 --- a/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go +++ b/internal/services/azurestackhci/stack_hci_deployment_setting_resource.go @@ -18,7 +18,7 @@ import ( "github.com/hashicorp/go-azure-sdk/resource-manager/hybridcompute/2022-11-10/machines" "github.com/hashicorp/go-azure-sdk/resource-manager/resourceconnector/2022-10-27/appliances" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" - storageValidate "github.com/hashicorp/terraform-provider-azurerm/internal/storage/validate" + storageValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" ) @@ -212,7 +212,7 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem Required: true, ForceNew: true, ValidateFunc: validation.StringMatch( - regex.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`), + regexp.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`), "`azure_service_endpoint` must be a valid domain name, for example, \"core.windows.net\"", ), }, @@ -249,7 +249,7 @@ func (StackHCIDeploymentSettingResource) Arguments() map[string]*pluginsdk.Schem Required: true, ForceNew: true, ValidateFunc: validation.StringMatch( - regex.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`), + regexp.MustCompile(`^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`), "`domain_fqdn` must be a valid domain name, for example, \"jumpstart.local\"", ), }, @@ -866,7 +866,7 @@ func (StackHCIDeploymentSettingResource) Delete() sdk.ResourceFunc { } } - customLocationsClient := metadata.Client.ExtendedLocation.customLocationsClient + customLocationsClient := metadata.Client.ExtendedLocation.CustomLocationsClient if err := customLocationsClient.DeleteThenPoll(ctx, customLocationId); err != nil { return fmt.Errorf("deleting the Custom Location generated during deployment: deleting %s: %+v", customLocationId, err) } diff --git a/internal/services/extendedlocation/client/client.go b/internal/services/extendedlocation/client/client.go index c792e7e737bb..a6ecdda41e33 100644 --- a/internal/services/extendedlocation/client/client.go +++ b/internal/services/extendedlocation/client/client.go @@ -11,7 +11,7 @@ import ( ) type Client struct { - CustomLocationsClient *customlocations.Client + CustomLocationsClient *customlocations.CustomLocationsClient } func NewClient(o *common.ClientOptions) (*Client, error) { diff --git a/website/docs/guides/features-block.html.markdown b/website/docs/guides/features-block.html.markdown index 65262349ed78..622b139922cb 100644 --- a/website/docs/guides/features-block.html.markdown +++ b/website/docs/guides/features-block.html.markdown @@ -113,8 +113,6 @@ The `features` block supports the following: * `application_insights` - (Optional) An `application_insights` block as defined below. -* `azure_stack_hci` - (Optional) An `azure_stack_hci` block as defined below. - * `cognitive_account` - (Optional) A `cognitive_account` block as defined below. * `key_vault` - (Optional) A `key_vault` block as defined below.