From 472faffd54f086793bdd0ae0aa81c01e8ff733bc Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Wed, 26 Jun 2024 08:47:16 +0000 Subject: [PATCH 1/5] new resource stack_hci_logical_network --- .github/labeler-issue-triage.yml | 2 +- .../services/azurestackhci/registration.go | 10 +- .../stack_hci_logical_network_resource.go | 478 ++++++++++++++++++ ...stack_hci_logical_network_resource_test.go | 270 ++++++++++ .../r/stack_hci_logical_network.html.markdown | 123 +++++ 5 files changed, 879 insertions(+), 4 deletions(-) create mode 100644 internal/services/azurestackhci/stack_hci_logical_network_resource.go create mode 100644 internal/services/azurestackhci/stack_hci_logical_network_resource_test.go create mode 100644 website/docs/r/stack_hci_logical_network.html.markdown diff --git a/.github/labeler-issue-triage.yml b/.github/labeler-issue-triage.yml index 8685e110217f..49706a2acac5 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..49f65474ad48 100644 --- a/internal/services/azurestackhci/registration.go +++ b/internal/services/azurestackhci/registration.go @@ -10,8 +10,10 @@ import ( type Registration struct{} -var _ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{} -var _ sdk.TypedServiceRegistrationWithAGitHubLabel = Registration{} +var ( + _ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{} + _ sdk.TypedServiceRegistrationWithAGitHubLabel = Registration{} +) func (r Registration) AssociatedGitHubLabel() string { return "service/azure-stack-hci" @@ -48,5 +50,7 @@ func (r Registration) DataSources() []sdk.DataSource { } func (r Registration) Resources() []sdk.Resource { - return []sdk.Resource{} + return []sdk.Resource{ + StackHCILogicalNetworkResource{}, + } } diff --git a/internal/services/azurestackhci/stack_hci_logical_network_resource.go b/internal/services/azurestackhci/stack_hci_logical_network_resource.go new file mode 100644 index 000000000000..a76f73f3f7a5 --- /dev/null +++ b/internal/services/azurestackhci/stack_hci_logical_network_resource.go @@ -0,0 +1,478 @@ +package azurestackhci + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/azurestackhci/2024-01-01/logicalnetworks" + "github.com/hashicorp/go-azure-sdk/resource-manager/extendedlocation/2021-08-15/customlocations" + "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 = StackHCILogicalNetworkResource{} + _ sdk.ResourceWithUpdate = StackHCILogicalNetworkResource{} +) + +type StackHCILogicalNetworkResource struct{} + +func (StackHCILogicalNetworkResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return logicalnetworks.ValidateLogicalNetworkID +} + +func (StackHCILogicalNetworkResource) ResourceType() string { + return "azurerm_stack_hci_logical_network" +} + +func (StackHCILogicalNetworkResource) ModelObject() interface{} { + return &StackHCILogicalNetworkResourceModel{} +} + +type StackHCILogicalNetworkResourceModel struct { + Name string `tfschema:"name"` + ResourceGroupName string `tfschema:"resource_group_name"` + Location string `tfschema:"location"` + CustomLocationId string `tfschema:"custom_location_id"` + DNSServers []string `tfschema:"dns_servers"` + Subnet []StackHCISubnetModel `tfschema:"subnet"` + VmSwitchName string `tfschema:"vm_switch_name"` + Tags map[string]interface{} `tfschema:"tags"` +} + +type StackHCISubnetModel struct { + AddressPrefix string `tfschema:"address_prefix"` + IpAllocationMethod string `tfschema:"ip_allocation_method"` + IpPool []StackHCIIPPoolModel `tfschema:"ip_pool"` + Route []StackHCIRouteModel `tfschema:"route"` + VlanId int64 `tfschema:"vlan_id"` +} + +type StackHCIIPPoolModel struct { + Start string `tfschema:"start"` + End string `tfschema:"end"` +} + +type StackHCIRouteModel struct { + Name string `tfschema:"name"` + AddressPrefix string `tfschema:"address_prefix"` + NextHopIpAddress string `tfschema:"next_hop_ip_address"` +} + +func (StackHCILogicalNetworkResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "resource_group_name": commonschema.ResourceGroupName(), + + "location": commonschema.Location(), + + "custom_location_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: customlocations.ValidateCustomLocationID, + }, + + "vm_switch_name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "dns_servers": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.IsIPv4Address, + }, + }, + + "subnet": { + Type: pluginsdk.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "ip_allocation_method": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(logicalnetworks.PossibleValuesForIPAllocationMethodEnum(), false), + }, + + "address_prefix": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IsCIDR, + }, + + "ip_pool": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "start": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.IsIPv4Address, + }, + "end": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.IsIPv4Address, + }, + }, + }, + }, + + "route": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "address_prefix": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IsCIDR, + }, + + "next_hop_ip_address": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IsIPv4Address, + }, + }, + }, + }, + + "vlan_id": { + Type: pluginsdk.TypeInt, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IntAtLeast(0), + }, + }, + }, + }, + + "tags": commonschema.Tags(), + } +} + +func (StackHCILogicalNetworkResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (r StackHCILogicalNetworkResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.AzureStackHCI.LogicalNetworks + + var config StackHCILogicalNetworkResourceModel + if err := metadata.Decode(&config); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + subscriptionId := metadata.Client.Account.SubscriptionId + id := logicalnetworks.NewLogicalNetworkID(subscriptionId, config.ResourceGroupName, config.Name) + + 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 := logicalnetworks.LogicalNetworks{ + Name: pointer.To(config.Name), + Location: location.Normalize(config.Location), + Tags: tags.Expand(config.Tags), + ExtendedLocation: &logicalnetworks.ExtendedLocation{ + Name: pointer.To(config.CustomLocationId), + Type: pointer.To(logicalnetworks.ExtendedLocationTypesCustomLocation), + }, + Properties: &logicalnetworks.LogicalNetworkProperties{ + VMSwitchName: pointer.To(config.VmSwitchName), + Subnets: expandStackHCILogicalNetworkSubnet(config.Subnet), + DhcpOptions: &logicalnetworks.LogicalNetworkPropertiesDhcpOptions{ + DnsServers: pointer.To(config.DNSServers), + }, + }, + } + + future, err := client.CreateOrUpdate(ctx, id, payload) + if err != nil { + return fmt.Errorf("performing create %s: %+v", id, err) + } + + metadata.SetID(id) + + if err := future.Poller.PollUntilDone(ctx); err != nil { + return fmt.Errorf("polling after create %s: %+v", id, err) + } + + return nil + }, + } +} + +func (r StackHCILogicalNetworkResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.AzureStackHCI.LogicalNetworks + + id, err := logicalnetworks.ParseLogicalNetworkID(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 := StackHCILogicalNetworkResourceModel{ + Name: id.LogicalNetworkName, + ResourceGroupName: id.ResourceGroupName, + } + + if model := resp.Model; model != nil { + schema.Location = location.Normalize(model.Location) + schema.Tags = tags.Flatten(model.Tags) + + if model.ExtendedLocation != nil && model.ExtendedLocation.Name != nil { + customLocationId, err := customlocations.ParseCustomLocationIDInsensitively(*model.ExtendedLocation.Name) + if err != nil { + return err + } + + schema.CustomLocationId = customLocationId.ID() + } + + if props := model.Properties; props != nil { + schema.Subnet = flattenStackHCILogicalNetworkSubnet(props.Subnets) + schema.VmSwitchName = pointer.From(props.VMSwitchName) + + if props.DhcpOptions != nil { + schema.DNSServers = pointer.From(props.DhcpOptions.DnsServers) + } + } + } + + return metadata.Encode(&schema) + }, + } +} + +func (r StackHCILogicalNetworkResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.AzureStackHCI.LogicalNetworks + + id, err := logicalnetworks.ParseLogicalNetworkID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + var model StackHCILogicalNetworkResourceModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + resp, err := client.Get(ctx, *id) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + parameters := resp.Model + if parameters == nil { + return fmt.Errorf("retrieving %s: model was nil", *id) + } + + if metadata.ResourceData.HasChange("tags") { + parameters.Tags = tags.Expand(model.Tags) + } + + if err := client.CreateOrUpdateThenPoll(ctx, *id, *parameters); err != nil { + return fmt.Errorf("updating %s: %+v", id, err) + } + return nil + }, + } +} + +func (r StackHCILogicalNetworkResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.AzureStackHCI.LogicalNetworks + + id, err := logicalnetworks.ParseLogicalNetworkID(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 expandStackHCILogicalNetworkSubnet(input []StackHCISubnetModel) *[]logicalnetworks.Subnet { + if len(input) == 0 { + return nil + } + + results := make([]logicalnetworks.Subnet, 0) + for _, v := range input { + results = append(results, logicalnetworks.Subnet{ + Properties: &logicalnetworks.SubnetPropertiesFormat{ + AddressPrefix: pointer.To(v.AddressPrefix), + IPAllocationMethod: pointer.To(logicalnetworks.IPAllocationMethodEnum(v.IpAllocationMethod)), + IPPools: expandStackHCILogicalNetworkIPPool(v.IpPool), + RouteTable: expandStackHCILogicalNetworkRouteTable(v.Route), + Vlan: pointer.To(v.VlanId), + }, + }) + } + + return &results +} + +func flattenStackHCILogicalNetworkSubnet(input *[]logicalnetworks.Subnet) []StackHCISubnetModel { + if input == nil { + return make([]StackHCISubnetModel, 0) + } + + results := make([]StackHCISubnetModel, 0) + for _, v := range *input { + if v.Properties != nil { + results = append(results, StackHCISubnetModel{ + AddressPrefix: pointer.From(v.Properties.AddressPrefix), + IpAllocationMethod: string(pointer.From(v.Properties.IPAllocationMethod)), + IpPool: flattenStackHCILogicalNetworkIPPool(v.Properties.IPPools), + Route: flattenStackHCILogicalNetworkRouteTable(v.Properties.RouteTable), + VlanId: pointer.From(v.Properties.Vlan), + }) + } + } + + return results +} + +func expandStackHCILogicalNetworkIPPool(input []StackHCIIPPoolModel) *[]logicalnetworks.IPPool { + if len(input) == 0 { + return nil + } + + results := make([]logicalnetworks.IPPool, 0) + for _, v := range input { + results = append(results, logicalnetworks.IPPool{ + Start: pointer.To(v.Start), + End: pointer.To(v.End), + }) + } + + return &results +} + +func flattenStackHCILogicalNetworkIPPool(input *[]logicalnetworks.IPPool) []StackHCIIPPoolModel { + if input == nil { + return make([]StackHCIIPPoolModel, 0) + } + + results := make([]StackHCIIPPoolModel, 0) + for _, v := range *input { + results = append(results, StackHCIIPPoolModel{ + Start: pointer.From(v.Start), + End: pointer.From(v.End), + }) + } + + return results +} + +func expandStackHCILogicalNetworkRouteTable(input []StackHCIRouteModel) *logicalnetworks.RouteTable { + if len(input) == 0 { + return nil + } + + routes := make([]logicalnetworks.Route, 0) + for _, v := range input { + routes = append(routes, logicalnetworks.Route{ + Name: pointer.To(v.Name), + Properties: &logicalnetworks.RoutePropertiesFormat{ + AddressPrefix: pointer.To(v.AddressPrefix), + NextHopIPAddress: pointer.To(v.NextHopIpAddress), + }, + }) + } + + return &logicalnetworks.RouteTable{ + Properties: &logicalnetworks.RouteTablePropertiesFormat{ + Routes: pointer.To(routes), + }, + } +} + +func flattenStackHCILogicalNetworkRouteTable(input *logicalnetworks.RouteTable) []StackHCIRouteModel { + if input == nil || input.Properties == nil || input.Properties.Routes == nil { + return make([]StackHCIRouteModel, 0) + } + + results := make([]StackHCIRouteModel, 0) + for _, v := range *input.Properties.Routes { + route := StackHCIRouteModel{ + Name: pointer.From(v.Name), + } + if v.Properties != nil { + route.AddressPrefix = pointer.From(v.Properties.AddressPrefix) + route.NextHopIpAddress = pointer.From(v.Properties.NextHopIPAddress) + } + results = append(results, route) + } + + return results +} diff --git a/internal/services/azurestackhci/stack_hci_logical_network_resource_test.go b/internal/services/azurestackhci/stack_hci_logical_network_resource_test.go new file mode 100644 index 000000000000..c9ecda1b902a --- /dev/null +++ b/internal/services/azurestackhci/stack_hci_logical_network_resource_test.go @@ -0,0 +1,270 @@ +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/logicalnetworks" + "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 StackHCILogicalNetworkResource struct{} + +// https://learn.microsoft.com/en-us/azure-stack/hci/manage/create-logical-networks?tabs=azurecli#prerequisites +// The resource can only be created on the customlocation generated after HCI deployment +const ( + customLocationIdEnv = "ARM_TEST_STACK_HCI_CUSTOM_LOCATION_ID" +) + +func TestAccStackHCILogicalNetwork_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_stack_hci_logical_network", "test") + r := StackHCILogicalNetworkResource{} + + if os.Getenv(customLocationIdEnv) == "" { + t.Skipf("skip the test as one or more of below environment variables are not specified: %q", customLocationIdEnv) + } + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccStackHCILogicalNetwork_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_stack_hci_logical_network", "test") + r := StackHCILogicalNetworkResource{} + + if os.Getenv(customLocationIdEnv) == "" { + t.Skipf("skip the test as one or more of below environment variables are not specified: %q", customLocationIdEnv) + } + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccStackHCILogicalNetwork_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_stack_hci_logical_network", "test") + r := StackHCILogicalNetworkResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccStackHCILogicalNetwork_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_stack_hci_logical_network", "test") + r := StackHCILogicalNetworkResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r StackHCILogicalNetworkResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + clusterClient := client.AzureStackHCI.LogicalNetworks + id, err := logicalnetworks.ParseLogicalNetworkID(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 StackHCILogicalNetworkResource) basic(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +provider "azurerm" { + features {} +} + +resource "azurerm_stack_hci_logical_network" "test" { + name = "acctest-ln-${var.random_string}" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = %q + vm_switch_name = "ConvergedSwitch(managementcompute)" + + subnet { + ip_allocation_method = "Dynamic" + } +} +`, template, os.Getenv(customLocationIdEnv)) +} + +func (r StackHCILogicalNetworkResource) requiresImport(data acceptance.TestData) string { + config := r.basic(data) + + return fmt.Sprintf(` +%s + +resource "azurerm_stack_hci_logical_network" "import" { + name = azurerm_stack_hci_logical_network.test.name + resource_group_name = azurerm_stack_hci_logical_network.test.resource_group_name + location = azurerm_stack_hci_logical_network.test.location + custom_location_id = azurerm_stack_hci_logical_network.test.custom_location_id + vm_switch_name = azurerm_stack_hci_logical_network.test.vm_switch_name + + subnet { + ip_allocation_method = azurerm_stack_hci_logical_network.test.subnet.0.ip_allocation_method + } +} +`, config) +} + +func (r StackHCILogicalNetworkResource) update(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +provider "azurerm" { + features {} +} + +resource "azurerm_stack_hci_logical_network" "test" { + name = "acctest-ln-${var.random_string}" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = %q + vm_switch_name = "ConvergedSwitch(managementcompute)" + dns_servers = ["10.0.0.7", "10.0.0.8"] + + subnet { + ip_allocation_method = "Static" + address_prefix = "10.0.0.0/24" + vlan_id = 123 + ip_pool { + start = "10.0.0.218" + end = "10.0.0.230" + } + ip_pool { + start = "10.0.0.234" + end = "10.0.0.239" + } + route { + name = "test-route" + address_prefix = "10.0.0.0/28" + next_hop_ip_address = "10.0.20.1" + } + route { + name = "test-route2" + address_prefix = "10.0.0.128/28" + next_hop_ip_address = "10.0.20.2" + } + } +} +`, template, os.Getenv(customLocationIdEnv)) +} + +func (r StackHCILogicalNetworkResource) complete(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +provider "azurerm" { + features {} +} + +resource "azurerm_stack_hci_logical_network" "test" { + name = "acctest-ln-${var.random_string}" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = %q + vm_switch_name = "ConvergedSwitch(managementcompute)" + dns_servers = ["10.0.0.7", "10.0.0.8"] + + subnet { + ip_allocation_method = "Static" + address_prefix = "10.0.0.0/24" + vlan_id = 123 + ip_pool { + start = "10.0.0.218" + end = "10.0.0.230" + } + ip_pool { + start = "10.0.0.234" + end = "10.0.0.239" + } + route { + name = "test-route" + address_prefix = "10.0.0.0/28" + next_hop_ip_address = "10.0.20.1" + } + route { + name = "test-route2" + address_prefix = "10.0.0.128/28" + next_hop_ip_address = "10.0.20.2" + } + } + + tags = { + foo = "bar" + } +} +`, template, os.Getenv(customLocationIdEnv)) +} + +func (r StackHCILogicalNetworkResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +variable "primary_location" { + default = %q +} + +variable "random_string" { + default = %q +} + +resource "azurerm_resource_group" "test" { + name = "acctest-hci-ln-${var.random_string}" + location = var.primary_location +} +`, data.Locations.Primary, data.RandomString) +} diff --git a/website/docs/r/stack_hci_logical_network.html.markdown b/website/docs/r/stack_hci_logical_network.html.markdown new file mode 100644 index 000000000000..59caea166d61 --- /dev/null +++ b/website/docs/r/stack_hci_logical_network.html.markdown @@ -0,0 +1,123 @@ +--- +subcategory: "Azure Stack HCI" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_stack_hci_logical_network" +description: |- + Manages an Azure Stack HCI Logical Network. +--- + +# azurerm_stack_hci_logical_network + +Manages an Azure Stack HCI Logical Network. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-rg" + location = "West Europe" +} + +resource "azurerm_stack_hci_logical_network" "example" { + name = "example-hci-ln" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + custom_location_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ExtendedLocation/customLocations/cl1" + vm_switch_name = "ConvergedSwitch(managementcompute)" + dns_servers = ["10.0.0.7", "10.0.0.8"] + + subnet { + ip_allocation_method = "Static" + address_prefix = "10.0.0.0/24" + ip_pool { + start = "10.0.0.218" + end = "10.0.0.230" + } + route { + name = "example-route" + address_prefix = "10.0.0.0/28" + next_hop_ip_address = "10.0.20.1" + } + vlan_id = 123 + } + + tags = { + foo = "bar" + } +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `name` - (Required) The name which should be used for this Azure Stack HCI Logical Network. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) The name of the Resource Group where the Azure Stack HCI Logical Network should exist. Changing this forces a new resource to be created. + +* `location` - (Required) The Azure Region where the Azure Stack HCI Logical Network should exist. Changing this forces a new resource to be created. + +* `custom_location_id` - (Required) The ID of Custom Location where the Azure Stack HCI Logical Network should exist. Changing this forces a new resource to be created. + +* `vm_switch_name` - (Required) The name of the network switch used to associate with the Azure Stack HCI Logical Network. Possible switch name can be retrieved following the [Azure documents](https://learn.microsoft.com/azure-stack/hci/manage/create-logical-networks?tabs=azurecli#prerequisites). Changing this forces a new resource to be created. + +* `subnet` - (Required) A `subnet` block as defined below. Changing this forces a new resource to be created. + +* `dns_servers` - (Optional) A list of IPv4 addresses of DNS servers available to VMs deployed in the Logical Networks. Changing this forces a new resource to be created. + +* `tags` - (Optional) A mapping of tags which should be assigned to the Azure Stack HCI Logical Network. + +--- + +A `ip_pool` block supports the following: + +* `start` - (Required) The IPv4 address of the start of the IP address pool. Changing this forces a new resource to be created. + +* `end` - (Required) The IPv4 address of the end of the IP address pool. Changing this forces a new resource to be created. + +--- + +A `route` block supports the following: + +* `name` - (Required) The name of the route. Changing this forces a new resource to be created. + +* `address_prefix` - (Optional) The Address in CIDR notation. Changing this forces a new resource to be created. + +* `next_hop_ip_address` - (Optional) The IPv4 address of the next hop. Changing this forces a new resource to be created. + +--- + +A `subnet` block supports the following: + +* `ip_allocation_method` - (Required) IP address allocation method for the subnet. Possible value is `Dynamic` or `Static`. Changing this forces a new resource to be created. + +* `address_prefix` - (Optional) The address prefix in CIDR notation. Changing this forces a new resource to be created. + +* `ip_pool` - (Optional) One or more `ip_pool` block as defined above. Changing this forces a new resource to be created. + +* `route` - (Optional) One or more `route` block as defined above. Changing this forces a new resource to be created. + +* `vlan_id` - (Optional) VLAN ID for the Logical Network. Changing this forces a new resource to be created. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The resource ID of the Azure Stack HCI Logical Network. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Azure Stack HCI Logical Network. +* `read` - (Defaults to 5 minutes) Used when retrieving the Azure Stack HCI Logical Network. +* `update` - (Defaults to 30 minutes) Used when updating the Azure Stack HCI Logical Network. +* `delete` - (Defaults to 30 minutes) Used when deleting the Azure Stack HCI Logical Network. + +## Import + +Azure Stack HCI Logical Networks can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_stack_hci_logical_network.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.AzureStackHCI/logicalNetworks/ln1 +``` From 3a4e5aae8c37d613adf4a38a1c4edd3b6d84e16e Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Wed, 3 Jul 2024 07:31:15 +0000 Subject: [PATCH 2/5] validate name property; update test; change route name to required; change vm_switch_name to virtual_switch_name; update doc --- .../stack_hci_logical_network_resource.go | 35 ++++---- ...stack_hci_logical_network_resource_test.go | 80 ++++++++++++++++--- .../r/stack_hci_logical_network.html.markdown | 12 +-- 3 files changed, 96 insertions(+), 31 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_logical_network_resource.go b/internal/services/azurestackhci/stack_hci_logical_network_resource.go index a76f73f3f7a5..9ffc6833586f 100644 --- a/internal/services/azurestackhci/stack_hci_logical_network_resource.go +++ b/internal/services/azurestackhci/stack_hci_logical_network_resource.go @@ -3,6 +3,7 @@ package azurestackhci import ( "context" "fmt" + "regexp" "time" "github.com/hashicorp/go-azure-helpers/lang/pointer" @@ -43,7 +44,7 @@ type StackHCILogicalNetworkResourceModel struct { CustomLocationId string `tfschema:"custom_location_id"` DNSServers []string `tfschema:"dns_servers"` Subnet []StackHCISubnetModel `tfschema:"subnet"` - VmSwitchName string `tfschema:"vm_switch_name"` + VirtualSwitchName string `tfschema:"virtual_switch_name"` Tags map[string]interface{} `tfschema:"tags"` } @@ -69,10 +70,13 @@ type StackHCIRouteModel struct { func (StackHCILogicalNetworkResource) Arguments() map[string]*pluginsdk.Schema { return map[string]*pluginsdk.Schema{ "name": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile(`^[a-zA-Z0-9][\-\.\_a-zA-Z0-9]{0,62}[a-zA-Z0-9]$`), + "name must be between 2 and 64 characters and can only contain alphanumberic characters, hyphen, dot and underline", + ), }, "resource_group_name": commonschema.ResourceGroupName(), @@ -86,7 +90,7 @@ func (StackHCILogicalNetworkResource) Arguments() map[string]*pluginsdk.Schema { ValidateFunc: customlocations.ValidateCustomLocationID, }, - "vm_switch_name": { + "virtual_switch_name": { Type: pluginsdk.TypeString, Required: true, ForceNew: true, @@ -153,22 +157,25 @@ func (StackHCILogicalNetworkResource) Arguments() map[string]*pluginsdk.Schema { Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "name": { - Type: pluginsdk.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile(`^[a-zA-Z0-9][\-\.\_a-zA-Z0-9]{0,78}[a-zA-Z0-9]$`), + "name must be between 2 and 80 characters and can only contain alphanumberic characters, hyphen, dot and underline", + ), }, "address_prefix": { Type: pluginsdk.TypeString, - Optional: true, + Required: true, ForceNew: true, ValidateFunc: validation.IsCIDR, }, "next_hop_ip_address": { Type: pluginsdk.TypeString, - Optional: true, + Required: true, ForceNew: true, ValidateFunc: validation.IsIPv4Address, }, @@ -225,7 +232,7 @@ func (r StackHCILogicalNetworkResource) Create() sdk.ResourceFunc { Type: pointer.To(logicalnetworks.ExtendedLocationTypesCustomLocation), }, Properties: &logicalnetworks.LogicalNetworkProperties{ - VMSwitchName: pointer.To(config.VmSwitchName), + VMSwitchName: pointer.To(config.VirtualSwitchName), Subnets: expandStackHCILogicalNetworkSubnet(config.Subnet), DhcpOptions: &logicalnetworks.LogicalNetworkPropertiesDhcpOptions{ DnsServers: pointer.To(config.DNSServers), @@ -289,7 +296,7 @@ func (r StackHCILogicalNetworkResource) Read() sdk.ResourceFunc { if props := model.Properties; props != nil { schema.Subnet = flattenStackHCILogicalNetworkSubnet(props.Subnets) - schema.VmSwitchName = pointer.From(props.VMSwitchName) + schema.VirtualSwitchName = pointer.From(props.VMSwitchName) if props.DhcpOptions != nil { schema.DNSServers = pointer.From(props.DhcpOptions.DnsServers) diff --git a/internal/services/azurestackhci/stack_hci_logical_network_resource_test.go b/internal/services/azurestackhci/stack_hci_logical_network_resource_test.go index c9ecda1b902a..92fcb70a8b38 100644 --- a/internal/services/azurestackhci/stack_hci_logical_network_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_logical_network_resource_test.go @@ -23,7 +23,7 @@ const ( customLocationIdEnv = "ARM_TEST_STACK_HCI_CUSTOM_LOCATION_ID" ) -func TestAccStackHCILogicalNetwork_basic(t *testing.T) { +func TestAccStackHCILogicalNetwork_dynamic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_stack_hci_logical_network", "test") r := StackHCILogicalNetworkResource{} @@ -33,7 +33,7 @@ func TestAccStackHCILogicalNetwork_basic(t *testing.T) { data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.basic(data), + Config: r.dynamic(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -52,7 +52,7 @@ func TestAccStackHCILogicalNetwork_update(t *testing.T) { data.ResourceTest(t, r, []acceptance.TestStep{ { - Config: r.update(data), + Config: r.basic(data), Check: acceptance.ComposeTestCheckFunc( check.That(data.ResourceName).ExistsInAzure(r), ), @@ -65,6 +65,20 @@ func TestAccStackHCILogicalNetwork_update(t *testing.T) { ), }, data.ImportStep(), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), }) } @@ -117,7 +131,7 @@ func (r StackHCILogicalNetworkResource) Exists(ctx context.Context, client *clie return utils.Bool(resp.Model != nil), nil } -func (r StackHCILogicalNetworkResource) basic(data acceptance.TestData) string { +func (r StackHCILogicalNetworkResource) dynamic(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` %s @@ -131,7 +145,7 @@ resource "azurerm_stack_hci_logical_network" "test" { resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location custom_location_id = %q - vm_switch_name = "ConvergedSwitch(managementcompute)" + virtual_switch_name = "ConvergedSwitch(managementcompute)" subnet { ip_allocation_method = "Dynamic" @@ -141,7 +155,7 @@ resource "azurerm_stack_hci_logical_network" "test" { } func (r StackHCILogicalNetworkResource) requiresImport(data acceptance.TestData) string { - config := r.basic(data) + config := r.dynamic(data) return fmt.Sprintf(` %s @@ -151,7 +165,7 @@ resource "azurerm_stack_hci_logical_network" "import" { resource_group_name = azurerm_stack_hci_logical_network.test.resource_group_name location = azurerm_stack_hci_logical_network.test.location custom_location_id = azurerm_stack_hci_logical_network.test.custom_location_id - vm_switch_name = azurerm_stack_hci_logical_network.test.vm_switch_name + virtual_switch_name = azurerm_stack_hci_logical_network.test.virtual_switch_name subnet { ip_allocation_method = azurerm_stack_hci_logical_network.test.subnet.0.ip_allocation_method @@ -160,6 +174,50 @@ resource "azurerm_stack_hci_logical_network" "import" { `, config) } +func (r StackHCILogicalNetworkResource) basic(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%s + +provider "azurerm" { + features {} +} + +resource "azurerm_stack_hci_logical_network" "test" { + name = "acctest-ln-${var.random_string}" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + custom_location_id = %q + virtual_switch_name = "ConvergedSwitch(managementcompute)" + dns_servers = ["10.0.0.7", "10.0.0.8"] + + subnet { + ip_allocation_method = "Static" + address_prefix = "10.0.0.0/24" + vlan_id = 123 + ip_pool { + start = "10.0.0.218" + end = "10.0.0.230" + } + ip_pool { + start = "10.0.0.234" + end = "10.0.0.239" + } + route { + name = "test-route" + address_prefix = "10.0.0.0/28" + next_hop_ip_address = "10.0.20.1" + } + route { + name = "test-route2" + address_prefix = "10.0.0.128/28" + next_hop_ip_address = "10.0.20.2" + } + } +} +`, template, os.Getenv(customLocationIdEnv)) +} + func (r StackHCILogicalNetworkResource) update(data acceptance.TestData) string { template := r.template(data) return fmt.Sprintf(` @@ -174,7 +232,7 @@ resource "azurerm_stack_hci_logical_network" "test" { resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location custom_location_id = %q - vm_switch_name = "ConvergedSwitch(managementcompute)" + virtual_switch_name = "ConvergedSwitch(managementcompute)" dns_servers = ["10.0.0.7", "10.0.0.8"] subnet { @@ -200,6 +258,10 @@ resource "azurerm_stack_hci_logical_network" "test" { next_hop_ip_address = "10.0.20.2" } } + + tags = { + foo = "bar" + } } `, template, os.Getenv(customLocationIdEnv)) } @@ -218,7 +280,7 @@ resource "azurerm_stack_hci_logical_network" "test" { resource_group_name = azurerm_resource_group.test.name location = azurerm_resource_group.test.location custom_location_id = %q - vm_switch_name = "ConvergedSwitch(managementcompute)" + virtual_switch_name = "ConvergedSwitch(managementcompute)" dns_servers = ["10.0.0.7", "10.0.0.8"] subnet { diff --git a/website/docs/r/stack_hci_logical_network.html.markdown b/website/docs/r/stack_hci_logical_network.html.markdown index 59caea166d61..8ca6336341da 100644 --- a/website/docs/r/stack_hci_logical_network.html.markdown +++ b/website/docs/r/stack_hci_logical_network.html.markdown @@ -23,19 +23,15 @@ resource "azurerm_stack_hci_logical_network" "example" { resource_group_name = azurerm_resource_group.example.name location = azurerm_resource_group.example.location custom_location_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg1/providers/Microsoft.ExtendedLocation/customLocations/cl1" - vm_switch_name = "ConvergedSwitch(managementcompute)" + virtual_switch_name = "ConvergedSwitch(managementcompute)" dns_servers = ["10.0.0.7", "10.0.0.8"] subnet { ip_allocation_method = "Static" address_prefix = "10.0.0.0/24" - ip_pool { - start = "10.0.0.218" - end = "10.0.0.230" - } route { name = "example-route" - address_prefix = "10.0.0.0/28" + address_prefix = "0.0.0.0/0" next_hop_ip_address = "10.0.20.1" } vlan_id = 123 @@ -59,7 +55,7 @@ The following arguments are supported: * `custom_location_id` - (Required) The ID of Custom Location where the Azure Stack HCI Logical Network should exist. Changing this forces a new resource to be created. -* `vm_switch_name` - (Required) The name of the network switch used to associate with the Azure Stack HCI Logical Network. Possible switch name can be retrieved following the [Azure documents](https://learn.microsoft.com/azure-stack/hci/manage/create-logical-networks?tabs=azurecli#prerequisites). Changing this forces a new resource to be created. +* `virtual_switch_name` - (Required) The name of the virtual switch on the cluster used to associate with the Azure Stack HCI Logical Network. Possible switch name can be retrieved following the [Azure documents](https://learn.microsoft.com/azure-stack/hci/manage/create-logical-networks?tabs=azurecli#prerequisites). Changing this forces a new resource to be created. * `subnet` - (Required) A `subnet` block as defined below. Changing this forces a new resource to be created. @@ -89,7 +85,7 @@ A `route` block supports the following: A `subnet` block supports the following: -* `ip_allocation_method` - (Required) IP address allocation method for the subnet. Possible value is `Dynamic` or `Static`. Changing this forces a new resource to be created. +* `ip_allocation_method` - (Required) IP address allocation method for the subnet. Possible values are `Dynamic` and `Static`. Changing this forces a new resource to be created. * `address_prefix` - (Optional) The address prefix in CIDR notation. Changing this forces a new resource to be created. From 6e38eae0677960fd827c05994f384a4292c11471 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Wed, 3 Jul 2024 07:58:33 +0000 Subject: [PATCH 3/5] fix update test --- .../azurestackhci/stack_hci_logical_network_resource_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/services/azurestackhci/stack_hci_logical_network_resource_test.go b/internal/services/azurestackhci/stack_hci_logical_network_resource_test.go index 92fcb70a8b38..8971dadedc16 100644 --- a/internal/services/azurestackhci/stack_hci_logical_network_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_logical_network_resource_test.go @@ -309,6 +309,7 @@ resource "azurerm_stack_hci_logical_network" "test" { tags = { foo = "bar" + env = "test" } } `, template, os.Getenv(customLocationIdEnv)) From 2214a62713c1032dee0f5e9095e72151399acaf2 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Thu, 11 Jul 2024 07:47:39 +0000 Subject: [PATCH 4/5] use createThenPoll; fix error message; fix doc --- .../stack_hci_logical_network_resource.go | 9 ++------- .../stack_hci_logical_network_resource_test.go | 13 ++++--------- .../docs/r/stack_hci_logical_network.html.markdown | 4 ++-- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/internal/services/azurestackhci/stack_hci_logical_network_resource.go b/internal/services/azurestackhci/stack_hci_logical_network_resource.go index 9ffc6833586f..02783a3588e4 100644 --- a/internal/services/azurestackhci/stack_hci_logical_network_resource.go +++ b/internal/services/azurestackhci/stack_hci_logical_network_resource.go @@ -240,17 +240,12 @@ func (r StackHCILogicalNetworkResource) Create() sdk.ResourceFunc { }, } - future, err := client.CreateOrUpdate(ctx, id, payload) - if err != nil { + if err := client.CreateOrUpdateThenPoll(ctx, id, payload); err != nil { return fmt.Errorf("performing create %s: %+v", id, err) } metadata.SetID(id) - if err := future.Poller.PollUntilDone(ctx); err != nil { - return fmt.Errorf("polling after create %s: %+v", id, err) - } - return nil }, } @@ -332,7 +327,7 @@ func (r StackHCILogicalNetworkResource) Update() sdk.ResourceFunc { parameters := resp.Model if parameters == nil { - return fmt.Errorf("retrieving %s: model was nil", *id) + return fmt.Errorf("retrieving %s: `model` was nil", *id) } if metadata.ResourceData.HasChange("tags") { diff --git a/internal/services/azurestackhci/stack_hci_logical_network_resource_test.go b/internal/services/azurestackhci/stack_hci_logical_network_resource_test.go index 8971dadedc16..6bef504e8c7a 100644 --- a/internal/services/azurestackhci/stack_hci_logical_network_resource_test.go +++ b/internal/services/azurestackhci/stack_hci_logical_network_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/logicalnetworks" "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 StackHCILogicalNetworkResource struct{} @@ -28,7 +27,7 @@ func TestAccStackHCILogicalNetwork_dynamic(t *testing.T) { r := StackHCILogicalNetworkResource{} if os.Getenv(customLocationIdEnv) == "" { - t.Skipf("skip the test as one or more of below environment variables are not specified: %q", customLocationIdEnv) + t.Skipf("skipping since %q has not been specified", customLocationIdEnv) } data.ResourceTest(t, r, []acceptance.TestStep{ @@ -47,7 +46,7 @@ func TestAccStackHCILogicalNetwork_update(t *testing.T) { r := StackHCILogicalNetworkResource{} if os.Getenv(customLocationIdEnv) == "" { - t.Skipf("skip the test as one or more of below environment variables are not specified: %q", customLocationIdEnv) + t.Skipf("skipping since %q has not been specified", customLocationIdEnv) } data.ResourceTest(t, r, []acceptance.TestStep{ @@ -121,14 +120,10 @@ func (r StackHCILogicalNetworkResource) Exists(ctx context.Context, client *clie 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 StackHCILogicalNetworkResource) dynamic(data acceptance.TestData) string { diff --git a/website/docs/r/stack_hci_logical_network.html.markdown b/website/docs/r/stack_hci_logical_network.html.markdown index 8ca6336341da..51879598f0cc 100644 --- a/website/docs/r/stack_hci_logical_network.html.markdown +++ b/website/docs/r/stack_hci_logical_network.html.markdown @@ -55,7 +55,7 @@ The following arguments are supported: * `custom_location_id` - (Required) The ID of Custom Location where the Azure Stack HCI Logical Network should exist. Changing this forces a new resource to be created. -* `virtual_switch_name` - (Required) The name of the virtual switch on the cluster used to associate with the Azure Stack HCI Logical Network. Possible switch name can be retrieved following the [Azure documents](https://learn.microsoft.com/azure-stack/hci/manage/create-logical-networks?tabs=azurecli#prerequisites). Changing this forces a new resource to be created. +* `virtual_switch_name` - (Required) The name of the virtual switch on the cluster used to associate with the Azure Stack HCI Logical Network. Possible switch names can be retrieved by following this [Azure guide](https://learn.microsoft.com/azure-stack/hci/manage/create-logical-networks?tabs=azurecli#prerequisites). Changing this forces a new resource to be created. * `subnet` - (Required) A `subnet` block as defined below. Changing this forces a new resource to be created. @@ -93,7 +93,7 @@ A `subnet` block supports the following: * `route` - (Optional) One or more `route` block as defined above. Changing this forces a new resource to be created. -* `vlan_id` - (Optional) VLAN ID for the Logical Network. Changing this forces a new resource to be created. +* `vlan_id` - (Optional) The VLAN ID for the Logical Network. Changing this forces a new resource to be created. ## Attributes Reference From d80ab4632da6663e5eb2eae94dbb03730e9d8dd1 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Thu, 11 Jul 2024 07:51:05 +0000 Subject: [PATCH 5/5] update doc --- website/docs/r/stack_hci_logical_network.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/stack_hci_logical_network.html.markdown b/website/docs/r/stack_hci_logical_network.html.markdown index 51879598f0cc..1aa18577438e 100644 --- a/website/docs/r/stack_hci_logical_network.html.markdown +++ b/website/docs/r/stack_hci_logical_network.html.markdown @@ -85,7 +85,7 @@ A `route` block supports the following: A `subnet` block supports the following: -* `ip_allocation_method` - (Required) IP address allocation method for the subnet. Possible values are `Dynamic` and `Static`. Changing this forces a new resource to be created. +* `ip_allocation_method` - (Required) The IP address allocation method for the subnet. Possible values are `Dynamic` and `Static`. Changing this forces a new resource to be created. * `address_prefix` - (Optional) The address prefix in CIDR notation. Changing this forces a new resource to be created.