diff --git a/internal/provider/services.go b/internal/provider/services.go index c4a042bdd07a..ee7fac76f8d1 100644 --- a/internal/provider/services.go +++ b/internal/provider/services.go @@ -161,6 +161,7 @@ func SupportedTypedServices() []sdk.TypedServiceRegistration { mssql.Registration{}, mssqlmanagedinstance.Registration{}, network.Registration{}, + netapp.Registration{}, nginx.Registration{}, policy.Registration{}, privatednsresolver.Registration{}, diff --git a/internal/services/netapp/client/client.go b/internal/services/netapp/client/client.go index 73e6e75dafe9..b79fa849afbf 100644 --- a/internal/services/netapp/client/client.go +++ b/internal/services/netapp/client/client.go @@ -5,6 +5,7 @@ import ( "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/netappaccounts" "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/snapshotpolicy" "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/snapshots" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups" "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumes" "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumesreplication" "github.com/hashicorp/terraform-provider-azurerm/internal/common" @@ -14,6 +15,7 @@ type Client struct { AccountClient *netappaccounts.NetAppAccountsClient PoolClient *capacitypools.CapacityPoolsClient VolumeClient *volumes.VolumesClient + VolumeGroupClient *volumegroups.VolumeGroupsClient VolumeReplicationClient *volumesreplication.VolumesReplicationClient SnapshotClient *snapshots.SnapshotsClient SnapshotPoliciesClient *snapshotpolicy.SnapshotPolicyClient @@ -29,6 +31,9 @@ func NewClient(o *common.ClientOptions) *Client { volumeClient := volumes.NewVolumesClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&volumeClient.Client, o.ResourceManagerAuthorizer) + volumeGroupClient := volumegroups.NewVolumeGroupsClientWithBaseURI(o.ResourceManagerEndpoint) + o.ConfigureClient(&volumeGroupClient.Client, o.ResourceManagerAuthorizer) + volumeReplicationClient := volumesreplication.NewVolumesReplicationClientWithBaseURI(o.ResourceManagerEndpoint) o.ConfigureClient(&volumeReplicationClient.Client, o.ResourceManagerAuthorizer) @@ -42,6 +47,7 @@ func NewClient(o *common.ClientOptions) *Client { AccountClient: &accountClient, PoolClient: &poolClient, VolumeClient: &volumeClient, + VolumeGroupClient: &volumeGroupClient, VolumeReplicationClient: &volumeReplicationClient, SnapshotClient: &snapshotClient, SnapshotPoliciesClient: &snapshotPoliciesClient, diff --git a/internal/services/netapp/netapp_volume_group_sap_hana_data_source.go b/internal/services/netapp/netapp_volume_group_sap_hana_data_source.go new file mode 100644 index 000000000000..fc2722012eb4 --- /dev/null +++ b/internal/services/netapp/netapp_volume_group_sap_hana_data_source.go @@ -0,0 +1,285 @@ +package netapp + +import ( + "context" + "fmt" + "net/http" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +type NetAppVolumeGroupSapHanaDataSourceModel struct { + Name string `tfschema:"name"` + ResourceGroupName string `tfschema:"resource_group_name"` + Location string `tfschema:"location"` + AccountName string `tfschema:"account_name"` + GroupDescription string `tfschema:"group_description"` + ApplicationIdentifier string `tfschema:"application_identifier"` + Volumes []NetAppVolumeGroupVolume `tfschema:"volume"` +} + +var _ sdk.DataSource = NetAppVolumeGroupSapHanaDataSource{} + +type NetAppVolumeGroupSapHanaDataSource struct{} + +func (r NetAppVolumeGroupSapHanaDataSource) ResourceType() string { + return "azurerm_netapp_volume_group_sap_hana" +} + +func (r NetAppVolumeGroupSapHanaDataSource) ModelObject() interface{} { + return &NetAppVolumeGroupSapHanaDataSourceModel{} +} + +func (r NetAppVolumeGroupSapHanaDataSource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return volumegroups.ValidateVolumeGroupID +} + +func (r NetAppVolumeGroupSapHanaDataSource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "resource_group_name": commonschema.ResourceGroupName(), + + "account_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + } +} + +func (r NetAppVolumeGroupSapHanaDataSource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "location": commonschema.LocationComputed(), + + "group_description": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "application_identifier": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "volume": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "capacity_pool_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "proximity_placement_group_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "volume_spec_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "volume_path": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "service_level": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "subnet_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "protocols": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "security_style": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "storage_quota_in_gb": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "throughput_in_mibps": { + Type: pluginsdk.TypeFloat, + Required: true, + }, + + "export_policy_rule": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "rule_index": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "allowed_clients": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "nfsv3_enabled": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + + "nfsv41_enabled": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + + "unix_read_only": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + + "unix_read_write": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + + "root_access_enabled": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + }, + }, + }, + + "tags": commonschema.TagsDataSource(), + + "snapshot_directory_visible": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + + "mount_ip_addresses": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "data_protection_replication": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "endpoint_type": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "remote_volume_location": commonschema.LocationComputed(), + + "remote_volume_resource_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "replication_frequency": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + + "data_protection_snapshot_policy": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "snapshot_policy_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + } +} + +func (r NetAppVolumeGroupSapHanaDataSource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + + client := metadata.Client.NetApp.VolumeGroupClient + + var state NetAppVolumeGroupSapHanaDataSourceModel + if err := metadata.Decode(&state); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + id := volumegroups.NewVolumeGroupID(metadata.Client.Account.SubscriptionId, state.ResourceGroupName, state.AccountName, state.Name) + + resp, err := client.VolumeGroupsGet(ctx, id) + if err != nil { + if resp.HttpResponse.StatusCode == http.StatusNotFound { + return metadata.MarkAsGone(id) + } + return fmt.Errorf("retrieving %s: %v", id, err) + } + + model := resp.Model + if model == nil { + return fmt.Errorf("retrieving %s: model was nil", id) + } + + state.Location = location.Normalize(pointer.From(model.Location)) + state.ApplicationIdentifier = pointer.From(model.Properties.GroupMetaData.ApplicationIdentifier) + state.GroupDescription = pointer.From(model.Properties.GroupMetaData.GroupDescription) + + volumes, err := flattenNetAppVolumeGroupVolumes(ctx, model.Properties.Volumes, metadata) + if err != nil { + return fmt.Errorf("setting `volume`: %+v", err) + } + + state.Volumes = volumes + + metadata.SetID(id) + + return metadata.Encode(&state) + }, + } +} diff --git a/internal/services/netapp/netapp_volume_group_sap_hana_data_source_test.go b/internal/services/netapp/netapp_volume_group_sap_hana_data_source_test.go new file mode 100644 index 000000000000..83ca739e910e --- /dev/null +++ b/internal/services/netapp/netapp_volume_group_sap_hana_data_source_test.go @@ -0,0 +1,39 @@ +package netapp_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type NetAppVolumeGroupSapHanaDataSource struct{} + +func TestAccNetAppVolumeGroupSapHanaDataSource_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_netapp_volume_group_sap_hana", "test") + d := NetAppVolumeGroupSapHanaDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: d.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("volume.1.volume_spec_name").HasValue("log"), + ), + }, + }) +} + +func (d NetAppVolumeGroupSapHanaDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_netapp_volume_group_sap_hana" "test" { + name = azurerm_netapp_volume_group_sap_hana.test.name + resource_group_name = azurerm_netapp_volume_group_sap_hana.test.resource_group_name + account_name = azurerm_netapp_volume_group_sap_hana.test.account_name +} +`, NetAppVolumeGroupSapHanaResource{}.basic(data)) +} diff --git a/internal/services/netapp/netapp_volume_group_sap_hana_resource.go b/internal/services/netapp/netapp_volume_group_sap_hana_resource.go new file mode 100644 index 000000000000..b862362c8f65 --- /dev/null +++ b/internal/services/netapp/netapp_volume_group_sap_hana_resource.go @@ -0,0 +1,641 @@ +package netapp + +import ( + "context" + "fmt" + "log" + "net/http" + "strings" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "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/netapp/2022-05-01/capacitypools" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumes" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumesreplication" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/netapp/validate" + netAppValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/netapp/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type NetAppVolumeGroupSapHanaResource struct{} + +type NetAppVolumeGroupSapHanaModel struct { + Name string `tfschema:"name"` + ResourceGroupName string `tfschema:"resource_group_name"` + Location string `tfschema:"location"` + AccountName string `tfschema:"account_name"` + GroupDescription string `tfschema:"group_description"` + ApplicationIdentifier string `tfschema:"application_identifier"` + Volumes []NetAppVolumeGroupVolume `tfschema:"volume"` +} + +var _ sdk.Resource = NetAppVolumeGroupSapHanaResource{} + +func (r NetAppVolumeGroupSapHanaResource) ModelObject() interface{} { + return &NetAppVolumeGroupSapHanaModel{} +} + +func (r NetAppVolumeGroupSapHanaResource) ResourceType() string { + return "azurerm_netapp_volume_group_sap_hana" +} + +func (r NetAppVolumeGroupSapHanaResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return volumegroups.ValidateVolumeGroupID +} + +func (r NetAppVolumeGroupSapHanaResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: netAppValidate.VolumeGroupName, + }, + + "resource_group_name": commonschema.ResourceGroupName(), + + "location": commonschema.Location(), + + "account_name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: netAppValidate.AccountName, + }, + + "group_description": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "application_identifier": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 3), + }, + + "volume": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 2, + MaxItems: 5, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: netAppValidate.VolumeName, + }, + + "capacity_pool_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "proximity_placement_group_id": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "volume_spec_name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(validate.PossibleValuesForVolumeSpecNameSapHana(), false), + }, + + "volume_path": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: netAppValidate.VolumePath, + }, + + "service_level": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(volumegroups.ServiceLevelPremium), + string(volumegroups.ServiceLevelStandard), + string(volumegroups.ServiceLevelUltra), + }, false), + }, + + "subnet_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "protocols": { + Type: pluginsdk.TypeList, + ForceNew: true, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringInSlice(validate.PossibleValuesForProtocolTypeVolumeGroupSapHana(), false), + }, + }, + + "security_style": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(validate.PossibleValuesForSecurityStyle(), false), + }, + + "storage_quota_in_gb": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(100, 102400), + }, + + "throughput_in_mibps": { + Type: pluginsdk.TypeFloat, + Required: true, + ValidateFunc: validation.FloatAtLeast(0.1), + }, + + "export_policy_rule": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + MaxItems: 5, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "rule_index": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 5), + }, + + "allowed_clients": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "nfsv3_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + }, + + "nfsv41_enabled": { + Type: pluginsdk.TypeBool, + Required: true, + }, + + "unix_read_only": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + + "unix_read_write": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + + "root_access_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + }, + }, + }, + + "tags": commonschema.Tags(), + + "snapshot_directory_visible": { + Type: pluginsdk.TypeBool, + Required: true, + ForceNew: true, + }, + + "mount_ip_addresses": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "data_protection_replication": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + ForceNew: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "endpoint_type": { + Type: pluginsdk.TypeString, + Optional: true, + Default: string(volumegroups.EndpointTypeDst), + ValidateFunc: validation.StringInSlice(volumegroups.PossibleValuesForEndpointType(), false), + }, + + "remote_volume_location": commonschema.LocationWithoutForceNew(), + + "remote_volume_resource_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + + "replication_frequency": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(PossibleValuesForReplicationSchedule(), false), + }, + }, + }, + }, + + "data_protection_snapshot_policy": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "snapshot_policy_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + }, + }, + }, + }, + }, + }, + } +} + +func (r NetAppVolumeGroupSapHanaResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (r NetAppVolumeGroupSapHanaResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 90 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.NetApp.VolumeGroupClient + replicationClient := metadata.Client.NetApp.VolumeReplicationClient + + subscriptionId := metadata.Client.Account.SubscriptionId + + var model NetAppVolumeGroupSapHanaModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + id := volumegroups.NewVolumeGroupID(subscriptionId, model.ResourceGroupName, model.AccountName, model.Name) + + metadata.Logger.Infof("Import check for %s", id) + existing, err := client.VolumeGroupsGet(ctx, id) + if err != nil && existing.HttpResponse.StatusCode != http.StatusNotFound { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + } + + if existing.Model != nil && existing.Model.Id != nil && *existing.Model.Id != "" { + return metadata.ResourceRequiresImport(r.ResourceType(), id) + } + + volumeList, err := expandNetAppVolumeGroupVolumes(model.Volumes) + if err != nil { + return err + } + + // Performing some basic validations that are not possible in the schema + if errorList := validate.ValidateNetAppVolumeGroupSAPHanaVolumes(volumeList); len(errorList) > 0 { + return fmt.Errorf("one or more issues found while performing deeper validations for %s:\n%+v", id, errorList) + } + + // Parse volume list to set secondary volumes for CRR + for i, volumeCrr := range pointer.From(volumeList) { + if volumeCrr.Properties.DataProtection != nil && + volumeCrr.Properties.DataProtection.Replication != nil && + strings.EqualFold(string(pointer.From(volumeCrr.Properties.DataProtection.Replication.EndpointType)), string(volumegroups.EndpointTypeDst)) { + + // Modify volumeType as data protection type on main volumeList + // so it gets created correctly as data protection volume + (pointer.From(volumeList))[i].Properties.VolumeType = utils.String("DataProtection") + } + } + + // TODO: deploymentSpecId is temporary until the backend is updated and deploymentSpecId is not required anymore, + // it will be handled internally by the RP + deploymentSpecId := "20542149-bfca-5618-1879-9863dc6767f1" + + parameters := volumegroups.VolumeGroupDetails{ + Location: utils.String(location.Normalize(model.Location)), + Properties: &volumegroups.VolumeGroupProperties{ + GroupMetaData: &volumegroups.VolumeGroupMetaData{ + GroupDescription: utils.String(model.GroupDescription), + ApplicationType: pointer.To(volumegroups.ApplicationTypeSAPNegativeHANA), + ApplicationIdentifier: utils.String(model.ApplicationIdentifier), + DeploymentSpecId: utils.String(deploymentSpecId), + }, + Volumes: volumeList, + }, + } + + err = client.VolumeGroupsCreateThenPoll(ctx, id, parameters) + if err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + // Waiting for volume group be completely provisioned + if err := waitForVolumeGroupCreateOrUpdate(ctx, client, id); err != nil { + return err + } + + // CRR - Authorizing secondaries from primary volumes + for _, volumeCrr := range pointer.From(volumeList) { + if volumeCrr.Properties.DataProtection != nil && + volumeCrr.Properties.DataProtection.Replication != nil && + strings.EqualFold(string(pointer.From(volumeCrr.Properties.DataProtection.Replication.EndpointType)), string(volumegroups.EndpointTypeDst)) { + + capacityPoolId, err := capacitypools.ParseCapacityPoolID(pointer.From(volumeCrr.Properties.CapacityPoolResourceId)) + if err != nil { + return err + } + + // Getting secondary volume resource id + secondaryId := volumes.NewVolumeID(subscriptionId, + model.ResourceGroupName, + model.AccountName, + capacityPoolId.CapacityPoolName, + getUserDefinedVolumeName(volumeCrr.Name), + ) + + // Getting primary resource id + primaryId, err := volumesreplication.ParseVolumeID(volumeCrr.Properties.DataProtection.Replication.RemoteVolumeResourceId) + if err != nil { + return err + } + + // Authorizing + if err = replicationClient.VolumesAuthorizeReplicationThenPoll(ctx, pointer.From(primaryId), volumesreplication.AuthorizeRequest{ + RemoteVolumeResourceId: utils.String(secondaryId.ID()), + }, + ); err != nil { + return fmt.Errorf("cannot authorize volume replication: %v", err) + } + + // Wait for volume replication authorization to complete + log.Printf("[DEBUG] Waiting for replication authorization on %s to complete", id) + if err := waitForReplAuthorization(ctx, replicationClient, pointer.From(primaryId)); err != nil { + return err + } + } + } + + metadata.SetID(id) + + return nil + }, + } +} + +func (r NetAppVolumeGroupSapHanaResource) Update() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 120 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + volumeClient := metadata.Client.NetApp.VolumeClient + + id, err := volumegroups.ParseVolumeGroupID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + metadata.Logger.Infof("Decoding state for %s", id) + var state NetAppVolumeGroupSapHanaModel + if err := metadata.Decode(&state); err != nil { + return err + } + + metadata.Logger.Infof("Updating %s", id) + + if metadata.ResourceData.HasChange("volume") { + + // Iterating over each volume and performing individual patch + for i := 0; i < metadata.ResourceData.Get("volume.#").(int); i++ { + + // Checking if individual volume has a change + volumeItem := fmt.Sprintf("volume.%v", i) + + capacityPoolId, err := capacitypools.ParseCapacityPoolID(metadata.ResourceData.Get(fmt.Sprintf("%v.capacity_pool_id", volumeItem)).(string)) + if err != nil { + return err + } + + if metadata.ResourceData.HasChange(volumeItem) { + + volumeId := volumes.NewVolumeID(id.SubscriptionId, + id.ResourceGroupName, + id.NetAppAccountName, + capacityPoolId.CapacityPoolName, + metadata.ResourceData.Get(fmt.Sprintf("%v.name", volumeItem)).(string)) + + update := volumes.VolumePatch{ + Properties: &volumes.VolumePatchProperties{}, + } + + if metadata.ResourceData.HasChange(fmt.Sprintf("%v.storage_quota_in_gb", volumeItem)) { + storageQuotaInBytes := int64(metadata.ResourceData.Get(fmt.Sprintf("%v.storage_quota_in_gb", volumeItem)).(int) * 1073741824) + update.Properties.UsageThreshold = utils.Int64(storageQuotaInBytes) + } + + if metadata.ResourceData.HasChange(fmt.Sprintf("%v.export_policy_rule", volumeItem)) { + exportPolicyRuleRaw := metadata.ResourceData.Get(fmt.Sprintf("%v.export_policy_rule", volumeItem)).([]interface{}) + + // Validating export policy rules + volumeProtocolRaw := (metadata.ResourceData.Get(fmt.Sprintf("%v.protocols", volumeItem)).([]interface{}))[0] + volumeProtocol := volumeProtocolRaw.(string) + + errors := make([]error, 0) + for _, ruleRaw := range exportPolicyRuleRaw { + if ruleRaw != nil { + rule := volumegroups.ExportPolicyRule{} + + v := ruleRaw.(map[string]interface{}) + rule.Nfsv3 = utils.Bool(v["nfsv3_enabled"].(bool)) + rule.Nfsv41 = utils.Bool(v["nfsv41_enabled"].(bool)) + + errors = append(errors, validate.ValidateNetAppVolumeGroupExportPolicyRuleSAPHanna(rule, volumeProtocol)...) + } + } + + if len(errors) > 0 { + return fmt.Errorf("one or more issues found while performing export policies validations for %s:\n%+v", id, errors) + } + + exportPolicyRule := expandNetAppVolumeGroupVolumeExportPolicyRulePatch(exportPolicyRuleRaw) + update.Properties.ExportPolicy = exportPolicyRule + } + + if metadata.ResourceData.HasChange(fmt.Sprintf("%v.data_protection_snapshot_policy", volumeItem)) { + // Validating that snapshot policies are not being created in a data protection volume + dataProtectionReplicationRaw := metadata.ResourceData.Get(fmt.Sprintf("%v.data_protection_replication", volumeItem)).([]interface{}) + dataProtectionReplication := expandNetAppVolumeDataProtectionReplication(dataProtectionReplicationRaw) + + if dataProtectionReplication != nil && + dataProtectionReplication.Replication != nil && + dataProtectionReplication.Replication.EndpointType != nil && + strings.EqualFold(string(pointer.From(dataProtectionReplication.Replication.EndpointType)), string(volumegroups.EndpointTypeDst)) { + + return fmt.Errorf("snapshot policy cannot be enabled on a data protection volume, %s", volumeId) + } + + dataProtectionSnapshotPolicyRaw := metadata.ResourceData.Get(fmt.Sprintf("%v.data_protection_snapshot_policy", volumeItem)).([]interface{}) + dataProtectionSnapshotPolicy := expandNetAppVolumeDataProtectionSnapshotPolicyPatch(dataProtectionSnapshotPolicyRaw) + update.Properties.DataProtection = dataProtectionSnapshotPolicy + } + + if metadata.ResourceData.HasChange(fmt.Sprintf("%v.throughput_in_mibps", volumeItem)) { + throughputMibps := metadata.ResourceData.Get(fmt.Sprintf("%v.throughput_in_mibps", volumeItem)) + update.Properties.ThroughputMibps = utils.Float(throughputMibps.(float64)) + } + + if metadata.ResourceData.HasChange(fmt.Sprintf("%v.tags", volumeItem)) { + tagsRaw := metadata.ResourceData.Get(fmt.Sprintf("%v.tags", volumeItem)).(map[string]interface{}) + update.Tags = tags.Expand(tagsRaw) + } + + if err = volumeClient.UpdateThenPoll(ctx, volumeId, update); err != nil { + return fmt.Errorf("updating %s: %+v", volumeId, err) + } + + // Wait for volume to complete update + if err := waitForVolumeCreateOrUpdate(ctx, volumeClient, volumeId); err != nil { + return err + } + } + } + } + + return nil + }, + } +} + +func (r NetAppVolumeGroupSapHanaResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + + client := metadata.Client.NetApp.VolumeGroupClient + + id, err := volumegroups.ParseVolumeGroupID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + metadata.Logger.Infof("Decoding state for %s", id) + var state NetAppVolumeGroupSapHanaModel + if err := metadata.Decode(&state); err != nil { + return err + } + + existing, err := client.VolumeGroupsGet(ctx, pointer.From(id)) + if err != nil { + if existing.HttpResponse.StatusCode == http.StatusNotFound { + return metadata.MarkAsGone(id) + } + return fmt.Errorf("retrieving %s: %v", id, err) + } + + metadata.SetID(id) + + model := NetAppVolumeGroupSapHanaModel{ + Name: id.VolumeGroupName, + AccountName: id.NetAppAccountName, + Location: location.NormalizeNilable(existing.Model.Location), + ResourceGroupName: id.ResourceGroupName, + } + + if props := existing.Model.Properties; props != nil { + model.GroupDescription = utils.NormalizeNilableString(props.GroupMetaData.GroupDescription) + model.ApplicationIdentifier = utils.NormalizeNilableString(props.GroupMetaData.ApplicationIdentifier) + + volumes, err := flattenNetAppVolumeGroupVolumes(ctx, props.Volumes, metadata) + if err != nil { + return fmt.Errorf("setting `volume`: %+v", err) + } + + model.Volumes = volumes + } + + return metadata.Encode(&model) + }, + } +} + +func (r NetAppVolumeGroupSapHanaResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 120 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + + client := metadata.Client.NetApp.VolumeGroupClient + + id, err := volumegroups.ParseVolumeGroupID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + existing, err := client.VolumeGroupsGet(ctx, pointer.From(id)) + if err != nil { + if existing.HttpResponse.StatusCode == http.StatusNotFound { + return metadata.MarkAsGone(id) + } + return fmt.Errorf("retrieving %s: %v", id, err) + } + + // Removing volumes before deleting volume group + if props := existing.Model.Properties; props != nil { + if volumeList := props.Volumes; volumeList != nil { + for _, volume := range *volumeList { + if err := deleteVolume(ctx, metadata, pointer.From(volume.Id)); err != nil { + return fmt.Errorf("deleting `volume`: %+v", err) + } + } + } + } + + // Removing Volume Group + if err = client.VolumeGroupsDeleteThenPoll(ctx, pointer.From(id)); err != nil { + return fmt.Errorf("deleting %s: %+v", pointer.From(id), err) + } + + return nil + }, + } +} diff --git a/internal/services/netapp/netapp_volume_group_sap_hana_resource_test.go b/internal/services/netapp/netapp_volume_group_sap_hana_resource_test.go new file mode 100644 index 000000000000..930467386f8f --- /dev/null +++ b/internal/services/netapp/netapp_volume_group_sap_hana_resource_test.go @@ -0,0 +1,1782 @@ +package netapp_test + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "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/utils" +) + +type NetAppVolumeGroupSapHanaResource struct{} + +func TestAccNetAppVolumeGroupSapHana_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_sap_hana", "test") + r := NetAppVolumeGroupSapHanaResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccNetAppVolumeGroupSapHana_backupVolumeSpecsNfsv3(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_sap_hana", "test") + r := NetAppVolumeGroupSapHanaResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.backupVolumeSpecsNfsv3(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccNetAppVolumeGroupSapHana_snapshotPolicy(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_sap_hana", "test") + r := NetAppVolumeGroupSapHanaResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.avgSnapshotPolicy(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccNetAppVolumeGroupSapHana_snapshotPolicyUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_sap_hana", "test") + r := NetAppVolumeGroupSapHanaResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.updateAvgSnapshotPolicy(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccNetAppVolumeGroupSapHana_volumeUpdates(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_sap_hana", "test") + r := NetAppVolumeGroupSapHanaResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.updateVolumes(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("volume.0.storage_quota_in_gb").HasValue("1200"), + check.That(data.ResourceName).Key("volume.1.export_policy_rule.0.allowed_clients").HasValue("10.0.0.0/8"), + check.That(data.ResourceName).Key("volume.2.tags.CreatedOnDate").HasValue("2022-07-27T12:00:00Z"), + check.That(data.ResourceName).Key("volume.4.storage_quota_in_gb").HasValue("1200"), + check.That(data.ResourceName).Key("volume.4.export_policy_rule.0.allowed_clients").HasValue("192.168.0.0/24"), + check.That(data.ResourceName).Key("volume.4.tags.CreatedOnDate").HasValue("2022-07-28T11:00:00Z"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccNetAppVolumeGroupSapHana_crossRegionReplication(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_sap_hana", "test_secondary") + r := NetAppVolumeGroupSapHanaResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.crossRegionReplication(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (t NetAppVolumeGroupSapHanaResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { + id, err := volumegroups.ParseVolumeGroupID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.NetApp.VolumeGroupClient.VolumeGroupsGet(ctx, *id) + + if err != nil { + if resp.HttpResponse.StatusCode == http.StatusNotFound { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving %s: %+v", id, err) + } + + return utils.Bool(true), nil +} + +func (NetAppVolumeGroupSapHanaResource) basic(data acceptance.TestData) string { + template := NetAppVolumeGroupSapHanaResource{}.templatePPG(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_netapp_volume_group_sap_hana" "test" { + name = "acctest-NetAppVolumeGroup-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + account_name = azurerm_netapp_account.test.name + group_description = "Test volume group" + application_identifier = "TST" + + volume { + name = "acctest-NetAppVolume-1-%[2]d" + volume_path = "my-unique-file-path-1-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "data" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-2-%[2]d" + volume_path = "my-unique-file-path-2-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "log" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-3-%[2]d" + volume_path = "my-unique-file-path-3-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "shared" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-4-%[2]d" + volume_path = "my-unique-file-path-4-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + volume_spec_name = "data-backup" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-5-%[2]d" + volume_path = "my-unique-file-path-5-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + volume_spec_name = "log-backup" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + depends_on = [ + azurerm_linux_virtual_machine.test, + azurerm_proximity_placement_group.test + ] +} +`, template, data.RandomInteger) +} + +func (NetAppVolumeGroupSapHanaResource) backupVolumeSpecsNfsv3(data acceptance.TestData) string { + template := NetAppVolumeGroupSapHanaResource{}.templatePPG(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_netapp_volume_group_sap_hana" "test" { + name = "acctest-NetAppVolumeGroup-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + account_name = azurerm_netapp_account.test.name + group_description = "Test volume group" + application_identifier = "TST" + + volume { + name = "acctest-NetAppVolume-1-%[2]d" + volume_path = "my-unique-file-path-1-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "data" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-2-%[2]d" + volume_path = "my-unique-file-path-2-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "log" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-3-%[2]d" + volume_path = "my-unique-file-path-3-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "shared" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-4-%[2]d" + volume_path = "my-unique-file-path-4-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + volume_spec_name = "data-backup" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv3"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = true + nfsv41_enabled = false + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-5-%[2]d" + volume_path = "my-unique-file-path-5-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + volume_spec_name = "log-backup" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv3"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = true + nfsv41_enabled = false + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + depends_on = [ + azurerm_linux_virtual_machine.test, + azurerm_proximity_placement_group.test + ] +} +`, template, data.RandomInteger) +} + +func (NetAppVolumeGroupSapHanaResource) avgSnapshotPolicy(data acceptance.TestData) string { + template := NetAppVolumeGroupSapHanaResource{}.templatePPG(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_netapp_snapshot_policy" "test" { + name = "acctest-NetAppSnapshotPolicy-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + account_name = azurerm_netapp_account.test.name + enabled = true + + monthly_schedule { + snapshots_to_keep = 1 + days_of_month = [15, 30] + hour = 23 + minute = 30 + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_netapp_volume_group_sap_hana" "test" { + name = "acctest-NetAppVolumeGroup-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + account_name = azurerm_netapp_account.test.name + group_description = "Test volume group" + application_identifier = "TST" + + volume { + name = "acctest-NetAppVolume-1-%[2]d" + volume_path = "my-unique-file-path-1-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "data" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + data_protection_snapshot_policy { + snapshot_policy_id = azurerm_netapp_snapshot_policy.test.id + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-2-%[2]d" + volume_path = "my-unique-file-path-2-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "log" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + data_protection_snapshot_policy { + snapshot_policy_id = azurerm_netapp_snapshot_policy.test.id + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-3-%[2]d" + volume_path = "my-unique-file-path-3-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "shared" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + data_protection_snapshot_policy { + snapshot_policy_id = azurerm_netapp_snapshot_policy.test.id + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-4-%[2]d" + volume_path = "my-unique-file-path-4-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + volume_spec_name = "data-backup" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + data_protection_snapshot_policy { + snapshot_policy_id = azurerm_netapp_snapshot_policy.test.id + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-5-%[2]d" + volume_path = "my-unique-file-path-5-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + volume_spec_name = "log-backup" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + data_protection_snapshot_policy { + snapshot_policy_id = azurerm_netapp_snapshot_policy.test.id + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + depends_on = [ + azurerm_linux_virtual_machine.test, + azurerm_proximity_placement_group.test + ] +} +`, template, data.RandomInteger) +} + +func (NetAppVolumeGroupSapHanaResource) updateAvgSnapshotPolicy(data acceptance.TestData) string { + template := NetAppVolumeGroupSapHanaResource{}.templatePPG(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_netapp_snapshot_policy" "test" { + name = "acctest-NetAppSnapshotPolicy-New-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + account_name = azurerm_netapp_account.test.name + enabled = true + + monthly_schedule { + snapshots_to_keep = 3 + days_of_month = [10, 25] + hour = 23 + minute = 30 + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_netapp_volume_group_sap_hana" "test" { + name = "acctest-NetAppVolumeGroup-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + account_name = azurerm_netapp_account.test.name + group_description = "Test volume group" + application_identifier = "TST" + + volume { + name = "acctest-NetAppVolume-1-%[2]d" + volume_path = "my-unique-file-path-1-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "data" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + data_protection_snapshot_policy { + snapshot_policy_id = azurerm_netapp_snapshot_policy.test.id + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-2-%[2]d" + volume_path = "my-unique-file-path-2-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "log" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + data_protection_snapshot_policy { + snapshot_policy_id = azurerm_netapp_snapshot_policy.test.id + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-3-%[2]d" + volume_path = "my-unique-file-path-3-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "shared" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + data_protection_snapshot_policy { + snapshot_policy_id = azurerm_netapp_snapshot_policy.test.id + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-4-%[2]d" + volume_path = "my-unique-file-path-4-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + volume_spec_name = "data-backup" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + data_protection_snapshot_policy { + snapshot_policy_id = azurerm_netapp_snapshot_policy.test.id + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-5-%[2]d" + volume_path = "my-unique-file-path-5-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + volume_spec_name = "log-backup" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + data_protection_snapshot_policy { + snapshot_policy_id = azurerm_netapp_snapshot_policy.test.id + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + depends_on = [ + azurerm_linux_virtual_machine.test, + azurerm_proximity_placement_group.test + ] +} +`, template, data.RandomInteger) +} + +func (NetAppVolumeGroupSapHanaResource) updateVolumes(data acceptance.TestData) string { + template := NetAppVolumeGroupSapHanaResource{}.templatePPG(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_netapp_volume_group_sap_hana" "test" { + name = "acctest-NetAppVolumeGroup-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + account_name = azurerm_netapp_account.test.name + group_description = "Test volume group" + application_identifier = "TST" + + volume { + name = "acctest-NetAppVolume-1-%[2]d" + volume_path = "my-unique-file-path-1-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "data" + storage_quota_in_gb = 1200 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-2-%[2]d" + volume_path = "my-unique-file-path-2-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "log" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "10.0.0.0/8" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-3-%[2]d" + volume_path = "my-unique-file-path-3-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "shared" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-27T12:00:00Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-4-%[2]d" + volume_path = "my-unique-file-path-4-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + volume_spec_name = "data-backup" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-5-%[2]d" + volume_path = "my-unique-file-path-5-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + volume_spec_name = "log-backup" + storage_quota_in_gb = 1200 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "192.168.0.0/24" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-28T11:00:00Z", + "SkipASMAzSecPack" = "true" + } + } + + depends_on = [ + azurerm_linux_virtual_machine.test, + azurerm_proximity_placement_group.test + ] +} +`, template, data.RandomInteger) +} + +func (NetAppVolumeGroupSapHanaResource) crossRegionReplication(data acceptance.TestData) string { + template := NetAppVolumeGroupSapHanaResource{}.templateForAvgCrossRegionReplication(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_netapp_volume_group_sap_hana" "test_primary" { + name = "acctest-NetAppVolumeGroup-Primary-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + account_name = azurerm_netapp_account.test.name + group_description = "Test volume group" + application_identifier = "TST" + + volume { + name = "acctest-NetAppVolume-1-Primary-%[2]d" + volume_path = "my-unique-file-path-1-Primary-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "data" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-2-Primary-%[2]d" + volume_path = "my-unique-file-path-2-Primary-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "log" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-3-Primary-%[2]d" + volume_path = "my-unique-file-path-3-Primary-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + volume_spec_name = "shared" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-4-Primary-%[2]d" + volume_path = "my-unique-file-path-4-Primary-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + volume_spec_name = "data-backup" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-5-Primary-%[2]d" + volume_path = "my-unique-file-path-5-Primary-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + volume_spec_name = "log-backup" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + depends_on = [ + azurerm_linux_virtual_machine.test, + azurerm_proximity_placement_group.test + ] +} + +resource "azurerm_netapp_volume_group_sap_hana" "test_secondary" { + name = "acctest-NetAppVolumeGroup-Secondary-%[2]d" + location = "%[3]s" + resource_group_name = azurerm_resource_group.test.name + account_name = azurerm_netapp_account.test_secondary.name + group_description = "Test volume group" + application_identifier = "TST" + + volume { + name = "acctest-NetAppVolume-1-Secondary-%[2]d" + volume_path = "my-unique-file-path-1-Secondary-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test_secondary.id + subnet_id = azurerm_subnet.test_secondary.id + proximity_placement_group_id = azurerm_proximity_placement_group.test_secondary.id + volume_spec_name = "data" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + data_protection_replication { + endpoint_type = "dst" + remote_volume_location = azurerm_netapp_volume_group_sap_hana.test_primary.location + remote_volume_resource_id = azurerm_netapp_volume_group_sap_hana.test_primary.volume[0].id + replication_frequency = "10minutes" + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-2-Secondary-%[2]d" + volume_path = "my-unique-file-path-2-Secondary-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test_secondary.id + subnet_id = azurerm_subnet.test_secondary.id + proximity_placement_group_id = azurerm_proximity_placement_group.test_secondary.id + volume_spec_name = "log" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-3-Secondary-%[2]d" + volume_path = "my-unique-file-path-3-Secondary-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test_secondary.id + subnet_id = azurerm_subnet.test_secondary.id + proximity_placement_group_id = azurerm_proximity_placement_group.test_secondary.id + volume_spec_name = "shared" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + data_protection_replication { + endpoint_type = "dst" + remote_volume_location = azurerm_netapp_volume_group_sap_hana.test_primary.location + remote_volume_resource_id = azurerm_netapp_volume_group_sap_hana.test_primary.volume[2].id + replication_frequency = "10minutes" + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-4-Secondary-%[2]d" + volume_path = "my-unique-file-path-4-Secondary-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test_secondary.id + subnet_id = azurerm_subnet.test_secondary.id + volume_spec_name = "data-backup" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + data_protection_replication { + endpoint_type = "dst" + remote_volume_location = azurerm_netapp_volume_group_sap_hana.test_primary.location + remote_volume_resource_id = azurerm_netapp_volume_group_sap_hana.test_primary.volume[3].id + replication_frequency = "10minutes" + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + volume { + name = "acctest-NetAppVolume-5-Secondary-%[2]d" + volume_path = "my-unique-file-path-5-Secondary-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test_secondary.id + subnet_id = azurerm_subnet.test_secondary.id + volume_spec_name = "log-backup" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + data_protection_replication { + endpoint_type = "dst" + remote_volume_location = azurerm_netapp_volume_group_sap_hana.test_primary.location + remote_volume_resource_id = azurerm_netapp_volume_group_sap_hana.test_primary.volume[4].id + replication_frequency = "10minutes" + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + } + + depends_on = [ + azurerm_linux_virtual_machine.test_secondary, + azurerm_proximity_placement_group.test_secondary, + + ] +} + + +`, template, data.RandomInteger, "eastus") +} + +func (r NetAppVolumeGroupSapHanaResource) templateForAvgCrossRegionReplication(data acceptance.TestData) string { + template := NetAppVolumeGroupSapHanaResource{}.templatePPG(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_network_security_group" "test_secondary" { + name = "acctest-NSG-Secondary-%[2]d" + location = "%[3]s" + resource_group_name = azurerm_resource_group.test.name + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_virtual_network" "test_secondary" { + name = "acctest-VirtualNetwork-Secondary-%[2]d" + location = "%[3]s" + resource_group_name = azurerm_resource_group.test.name + address_space = ["10.6.0.0/16"] + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_subnet" "test_secondary" { + name = "acctest-DelegatedSubnet-%[2]d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test_secondary.name + address_prefixes = ["10.6.2.0/24"] + + delegation { + name = "testdelegation" + + service_delegation { + name = "Microsoft.Netapp/volumes" + actions = ["Microsoft.Network/networkinterfaces/*", "Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +resource "azurerm_subnet" "test1_secondary" { + name = "acctest-HostsSubnet-%[2]d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test_secondary.name + address_prefixes = ["10.6.1.0/24"] +} + +resource "azurerm_subnet_network_security_group_association" "test_secondary" { + subnet_id = azurerm_subnet.test1_secondary.id + network_security_group_id = azurerm_network_security_group.test_secondary.id +} + +resource "azurerm_proximity_placement_group" "test_secondary" { + name = "acctest-PPG-Secondary-%[2]d" + location = "%[3]s" + resource_group_name = azurerm_resource_group.test.name + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_availability_set" "test_secondary" { + name = "acctest-avset-Secondary-%[2]d" + location = "%[3]s" + resource_group_name = azurerm_resource_group.test.name + + proximity_placement_group_id = azurerm_proximity_placement_group.test_secondary.id + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_network_interface" "test_secondary" { + name = "acctest-nic-Secondary-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = "%[3]s" + + ip_configuration { + name = "internal" + subnet_id = azurerm_subnet.test1_secondary.id + private_ip_address_allocation = "Dynamic" + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_linux_virtual_machine" "test_secondary" { + name = "acctest-vm-secondary-%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = "%[3]s" + size = "Standard_M8ms" + admin_username = local.admin_username + admin_password = local.admin_password + disable_password_authentication = false + proximity_placement_group_id = azurerm_proximity_placement_group.test_secondary.id + availability_set_id = azurerm_availability_set.test_secondary.id + network_interface_ids = [ + azurerm_network_interface.test_secondary.id + ] + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "18.04-LTS" + version = "latest" + } + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } + + tags = { + "platformsettings.host_environment.service.platform_optedin_for_rootcerts" = "true", + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true", + "Owner" = "pmarques" + } +} + +resource "azurerm_netapp_account" "test_secondary" { + name = "acctest-NetAppAccount-Secondary-%[2]d" + location = "%[3]s" + resource_group_name = azurerm_resource_group.test.name + + depends_on = [ + azurerm_subnet.test_secondary, + azurerm_subnet.test1_secondary + ] + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_netapp_pool" "test_secondary" { + name = "acctest-NetAppPool-Secondary-%[2]d" + location = "%[3]s" + resource_group_name = azurerm_resource_group.test.name + account_name = azurerm_netapp_account.test_secondary.name + service_level = "Standard" + size_in_tb = 8 + qos_type = "Manual" + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} +`, template, data.RandomInteger, "eastus") +} + +func (NetAppVolumeGroupSapHanaResource) templatePPG(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + alias = "all2" + features { + resource_group { + prevent_deletion_if_contains_resources = false + } + } +} + +locals { + admin_username = "testadmin%[1]d" + admin_password = "Password1234!%[1]d" +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-netapp-%[1]d" + location = "%[2]s" + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true", + "SkipNRMSNSG" = "true" + } +} + +resource "azurerm_network_security_group" "test" { + name = "acctest-NSG-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_virtual_network" "test" { + name = "acctest-VirtualNetwork-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + address_space = ["10.6.0.0/16"] + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_subnet" "test" { + name = "acctest-DelegatedSubnet-%[1]d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.6.2.0/24"] + + delegation { + name = "testdelegation" + + service_delegation { + name = "Microsoft.Netapp/volumes" + actions = ["Microsoft.Network/networkinterfaces/*", "Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +resource "azurerm_subnet" "test1" { + name = "acctest-HostsSubnet-%[1]d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.6.1.0/24"] +} + +resource "azurerm_subnet_network_security_group_association" "public" { + subnet_id = azurerm_subnet.test.id + network_security_group_id = azurerm_network_security_group.test.id +} + +resource "azurerm_proximity_placement_group" "test" { + name = "acctest-PPG-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_availability_set" "test" { + name = "acctest-avset-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_network_interface" "test" { + name = "acctest-nic-%[1]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + + ip_configuration { + name = "internal" + subnet_id = azurerm_subnet.test1.id + private_ip_address_allocation = "Dynamic" + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_linux_virtual_machine" "test" { + name = "acctest-vm-%[1]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + size = "Standard_M8ms" + admin_username = local.admin_username + admin_password = local.admin_password + disable_password_authentication = false + proximity_placement_group_id = azurerm_proximity_placement_group.test.id + availability_set_id = azurerm_availability_set.test.id + network_interface_ids = [ + azurerm_network_interface.test.id + ] + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "18.04-LTS" + version = "latest" + } + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } + + tags = { + "platformsettings.host_environment.service.platform_optedin_for_rootcerts" = "true", + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true", + "Owner" = "pmarques" + } +} + +resource "azurerm_netapp_account" "test" { + name = "acctest-NetAppAccount-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + depends_on = [ + azurerm_subnet.test, + azurerm_subnet.test1 + ] + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_netapp_pool" "test" { + name = "acctest-NetAppPool-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + account_name = azurerm_netapp_account.test.name + service_level = "Standard" + size_in_tb = 8 + qos_type = "Manual" + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} +`, data.RandomInteger, "westus") +} diff --git a/internal/services/netapp/netapp_volume_helper.go b/internal/services/netapp/netapp_volume_helper.go new file mode 100644 index 000000000000..66bb1e3aaa27 --- /dev/null +++ b/internal/services/netapp/netapp_volume_helper.go @@ -0,0 +1,793 @@ +package netapp + +import ( + "context" + "fmt" + "net/http" + "strconv" + "strings" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumes" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumesreplication" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type NetAppVolumeGroupVolume struct { + Id string `tfschema:"id"` + Name string `tfschema:"name"` + VolumePath string `tfschema:"volume_path"` + ServiceLevel string `tfschema:"service_level"` + SubnetId string `tfschema:"subnet_id"` + Protocols []string `tfschema:"protocols"` + SecurityStyle string `tfschema:"security_style"` + StorageQuotaInGB int64 `tfschema:"storage_quota_in_gb"` + ThroughputInMibps float64 `tfschema:"throughput_in_mibps"` + Tags map[string]string `tfschema:"tags"` + SnapshotDirectoryVisible bool `tfschema:"snapshot_directory_visible"` + CapacityPoolId string `tfschema:"capacity_pool_id"` + ProximityPlacementGroupId string `tfschema:"proximity_placement_group_id"` + VolumeSpecName string `tfschema:"volume_spec_name"` + ExportPolicy []ExportPolicyRule `tfschema:"export_policy_rule"` + MountIpAddresses []string `tfschema:"mount_ip_addresses"` + DataProtectionReplication []DataProtectionReplication `tfschema:"data_protection_replication"` + DataProtectionSnapshotPolicy []DataProtectionSnapshotPolicy `tfschema:"data_protection_snapshot_policy"` +} + +type ExportPolicyRule struct { + RuleIndex int `tfschema:"rule_index"` + AllowedClients string `tfschema:"allowed_clients"` + Nfsv3Enabled bool `tfschema:"nfsv3_enabled"` + Nfsv41Enabled bool `tfschema:"nfsv41_enabled"` + UnixReadOnly bool `tfschema:"unix_read_only"` + UnixReadWrite bool `tfschema:"unix_read_write"` + RootAccessEnabled bool `tfschema:"root_access_enabled"` +} + +type DataProtectionReplication struct { + EndpointType string `tfschema:"endpoint_type"` + RemoteVolumeLocation string `tfschema:"remote_volume_location"` + RemoteVolumeResourceId string `tfschema:"remote_volume_resource_id"` + ReplicationFrequency string `tfschema:"replication_frequency"` +} + +type DataProtectionSnapshotPolicy struct { + DataProtectionSnapshotPolicy string `tfschema:"snapshot_policy_id"` +} + +type ReplicationSchedule string + +const ( + ReplicationSchedule10Minutes ReplicationSchedule = "10minutes" + ReplicationScheduleDaily ReplicationSchedule = "daily" + ReplicationScheduleHourly ReplicationSchedule = "hourly" +) + +func PossibleValuesForReplicationSchedule() []string { + return []string{ + string(ReplicationSchedule10Minutes), + string(ReplicationScheduleDaily), + string(ReplicationScheduleHourly), + } +} + +func expandNetAppVolumeGroupVolumeExportPolicyRule(input []ExportPolicyRule) *volumegroups.VolumePropertiesExportPolicy { + + if len(input) == 0 { + return &volumegroups.VolumePropertiesExportPolicy{} + } + + results := make([]volumegroups.ExportPolicyRule, 0) + + for _, item := range input { + + // Hard-Coded values, for AVG these cannot be set differently + // they are not exposed as TF configuration + // but PUT request requires those fields to succeed + cifsEnabled := false + kerberos5ReadOnly := false + kerberos5ReadWrite := false + kerberos5iReadOnly := false + kerberos5iReadWrite := false + kerberos5pReadOnly := false + kerberos5pReadWrite := false + + result := volumegroups.ExportPolicyRule{ + AllowedClients: utils.String(item.AllowedClients), + Cifs: utils.Bool(cifsEnabled), + Nfsv3: utils.Bool(item.Nfsv3Enabled), + Nfsv41: utils.Bool(item.Nfsv41Enabled), + RuleIndex: utils.Int64(int64(item.RuleIndex)), + UnixReadOnly: utils.Bool(item.UnixReadOnly), + UnixReadWrite: utils.Bool(item.UnixReadWrite), + HasRootAccess: utils.Bool(item.RootAccessEnabled), + Kerberos5ReadOnly: utils.Bool(kerberos5ReadOnly), + Kerberos5ReadWrite: utils.Bool(kerberos5ReadWrite), + Kerberos5iReadOnly: utils.Bool(kerberos5iReadOnly), + Kerberos5iReadWrite: utils.Bool(kerberos5iReadWrite), + Kerberos5pReadOnly: utils.Bool(kerberos5pReadOnly), + Kerberos5pReadWrite: utils.Bool(kerberos5pReadWrite), + } + + results = append(results, result) + } + + return &volumegroups.VolumePropertiesExportPolicy{ + Rules: &results, + } +} + +func expandNetAppVolumeGroupDataProtectionReplication(input []DataProtectionReplication) *volumegroups.VolumePropertiesDataProtection { + if len(input) == 0 { + return &volumegroups.VolumePropertiesDataProtection{} + } + + replicationObject := volumegroups.ReplicationObject{} + + endpointType := volumegroups.EndpointType(input[0].EndpointType) + replicationObject.EndpointType = &endpointType + + replicationObject.RemoteVolumeRegion = &input[0].RemoteVolumeLocation + replicationObject.RemoteVolumeResourceId = input[0].RemoteVolumeResourceId + + replicationSchedule := volumegroups.ReplicationSchedule(translateTFSchedule(input[0].ReplicationFrequency)) + replicationObject.ReplicationSchedule = &replicationSchedule + + return &volumegroups.VolumePropertiesDataProtection{ + Replication: &replicationObject, + } +} + +func expandNetAppVolumeGroupDataProtectionSnapshotPolicy(input []DataProtectionSnapshotPolicy) *volumegroups.VolumePropertiesDataProtection { + if len(input) == 0 { + return &volumegroups.VolumePropertiesDataProtection{} + } + + snapshotObject := volumegroups.VolumeSnapshotProperties{} + snapshotObject.SnapshotPolicyId = &input[0].DataProtectionSnapshotPolicy + + return &volumegroups.VolumePropertiesDataProtection{ + Snapshot: &snapshotObject, + } +} + +func expandNetAppVolumeGroupVolumes(input []NetAppVolumeGroupVolume) (*[]volumegroups.VolumeGroupVolumeProperties, error) { + if len(input) == 0 { + return &[]volumegroups.VolumeGroupVolumeProperties{}, fmt.Errorf("received empty NetAppVolumeGroupVolume slice") + } + + results := make([]volumegroups.VolumeGroupVolumeProperties, 0) + + for _, item := range input { + name := item.Name + volumePath := item.VolumePath + serviceLevel := volumegroups.ServiceLevel(item.ServiceLevel) + subnetID := item.SubnetId + capacityPoolID := item.CapacityPoolId + protocols := item.Protocols + snapshotDirectoryVisible := item.SnapshotDirectoryVisible + securityStyle := volumegroups.SecurityStyle(item.SecurityStyle) + storageQuotaInGB := item.StorageQuotaInGB * 1073741824 + proximityPlacementGroupId := utils.NormalizeNilableString(&item.ProximityPlacementGroupId) + exportPolicyRule := expandNetAppVolumeGroupVolumeExportPolicyRule(item.ExportPolicy) + dataProtectionReplication := expandNetAppVolumeGroupDataProtectionReplication(item.DataProtectionReplication) + dataProtectionSnapshotPolicy := expandNetAppVolumeGroupDataProtectionSnapshotPolicy(item.DataProtectionSnapshotPolicy) + + volumeProperties := &volumegroups.VolumeGroupVolumeProperties{ + Name: utils.String(name), + Properties: volumegroups.VolumeProperties{ + CapacityPoolResourceId: utils.String(capacityPoolID), + CreationToken: volumePath, + ServiceLevel: &serviceLevel, + SubnetId: subnetID, + ProtocolTypes: &protocols, + SecurityStyle: &securityStyle, + UsageThreshold: storageQuotaInGB, + ExportPolicy: exportPolicyRule, + SnapshotDirectoryVisible: utils.Bool(snapshotDirectoryVisible), + ThroughputMibps: utils.Float(item.ThroughputInMibps), + ProximityPlacementGroup: &proximityPlacementGroupId, + VolumeSpecName: utils.String(item.VolumeSpecName), + DataProtection: &volumegroups.VolumePropertiesDataProtection{ + Replication: dataProtectionReplication.Replication, + Snapshot: dataProtectionSnapshotPolicy.Snapshot, + }, + }, + Tags: &item.Tags, + } + + results = append(results, *volumeProperties) + } + + return &results, nil +} + +func expandNetAppVolumeGroupVolumeExportPolicyRulePatch(input []interface{}) *volumes.VolumePatchPropertiesExportPolicy { + if len(input) == 0 { + return &volumes.VolumePatchPropertiesExportPolicy{} + } + + results := make([]volumes.ExportPolicyRule, 0) + for _, item := range input { + if item != nil { + v := item.(map[string]interface{}) + + ruleIndex := int64(v["rule_index"].(int)) + allowedClients := v["allowed_clients"].(string) + nfsv3Enabled := v["nfsv3_enabled"].(bool) + nfsv41Enabled := v["nfsv41_enabled"].(bool) + unixReadOnly := v["unix_read_only"].(bool) + unixReadWrite := v["unix_read_write"].(bool) + rootAccessEnabled := v["root_access_enabled"].(bool) + + // Hard-Coded values, for AVG these cannot be set differently + // they are not exposed as TF configuration + // but PUT request requires those fields to succeed + cifsEnabled := false + kerberos5ReadOnly := false + kerberos5ReadWrite := false + kerberos5iReadOnly := false + kerberos5iReadWrite := false + kerberos5pReadOnly := false + kerberos5pReadWrite := false + + result := volumes.ExportPolicyRule{ + AllowedClients: utils.String(allowedClients), + Cifs: utils.Bool(cifsEnabled), + Nfsv3: utils.Bool(nfsv3Enabled), + Nfsv41: utils.Bool(nfsv41Enabled), + RuleIndex: utils.Int64(ruleIndex), + UnixReadOnly: utils.Bool(unixReadOnly), + UnixReadWrite: utils.Bool(unixReadWrite), + HasRootAccess: utils.Bool(rootAccessEnabled), + Kerberos5ReadOnly: utils.Bool(kerberos5ReadOnly), + Kerberos5ReadWrite: utils.Bool(kerberos5ReadWrite), + Kerberos5iReadOnly: utils.Bool(kerberos5iReadOnly), + Kerberos5iReadWrite: utils.Bool(kerberos5iReadWrite), + Kerberos5pReadOnly: utils.Bool(kerberos5pReadOnly), + Kerberos5pReadWrite: utils.Bool(kerberos5pReadWrite), + } + + results = append(results, result) + } + } + + return &volumes.VolumePatchPropertiesExportPolicy{ + Rules: &results, + } +} + +func expandNetAppVolumeDataProtectionReplication(input []interface{}) *volumes.VolumePropertiesDataProtection { + if len(input) == 0 { + return &volumes.VolumePropertiesDataProtection{} + } + + replicationObject := volumes.ReplicationObject{} + + replicationRaw := input[0].(map[string]interface{}) + + if v, ok := replicationRaw["endpoint_type"]; ok { + endpointType := volumes.EndpointType(v.(string)) + replicationObject.EndpointType = &endpointType + } + if v, ok := replicationRaw["remote_volume_location"]; ok { + replicationObject.RemoteVolumeRegion = utils.String(v.(string)) + } + if v, ok := replicationRaw["remote_volume_resource_id"]; ok { + replicationObject.RemoteVolumeResourceId = v.(string) + } + if v, ok := replicationRaw["replication_frequency"]; ok { + replicationSchedule := volumes.ReplicationSchedule(translateTFSchedule(v.(string))) + replicationObject.ReplicationSchedule = &replicationSchedule + } + + return &volumes.VolumePropertiesDataProtection{ + Replication: &replicationObject, + } +} + +func expandNetAppVolumeDataProtectionSnapshotPolicy(input []interface{}) *volumes.VolumePropertiesDataProtection { + if len(input) == 0 { + return &volumes.VolumePropertiesDataProtection{} + } + + snapshotObject := volumes.VolumeSnapshotProperties{} + + snapshotRaw := input[0].(map[string]interface{}) + + if v, ok := snapshotRaw["snapshot_policy_id"]; ok { + snapshotObject.SnapshotPolicyId = utils.String(v.(string)) + } + + return &volumes.VolumePropertiesDataProtection{ + Snapshot: &snapshotObject, + } +} + +func expandNetAppVolumeDataProtectionSnapshotPolicyPatch(input []interface{}) *volumes.VolumePatchPropertiesDataProtection { + if len(input) == 0 { + return &volumes.VolumePatchPropertiesDataProtection{} + } + + snapshotObject := volumes.VolumeSnapshotProperties{} + + snapshotRaw := input[0].(map[string]interface{}) + + if v, ok := snapshotRaw["snapshot_policy_id"]; ok { + snapshotObject.SnapshotPolicyId = utils.String(v.(string)) + } + + return &volumes.VolumePatchPropertiesDataProtection{ + Snapshot: &snapshotObject, + } +} + +func flattenNetAppVolumeGroupVolumes(ctx context.Context, input *[]volumegroups.VolumeGroupVolumeProperties, metadata sdk.ResourceMetaData) ([]NetAppVolumeGroupVolume, error) { + results := make([]NetAppVolumeGroupVolume, 0) + + if input == nil || len(pointer.From(input)) == 0 { + return results, fmt.Errorf("received empty volumegroups.VolumeGroupVolumeProperties slice") + } + + for _, item := range *input { + volumeGroupVolume := NetAppVolumeGroupVolume{} + + props := item.Properties + volumeGroupVolume.Name = getUserDefinedVolumeName(item.Name) + volumeGroupVolume.VolumePath = props.CreationToken + volumeGroupVolume.ServiceLevel = string(pointer.From(props.ServiceLevel)) + volumeGroupVolume.SubnetId = props.SubnetId + volumeGroupVolume.CapacityPoolId = utils.NormalizeNilableString(props.CapacityPoolResourceId) + volumeGroupVolume.Protocols = pointer.From(props.ProtocolTypes) + volumeGroupVolume.SecurityStyle = string(pointer.From(props.SecurityStyle)) + volumeGroupVolume.SnapshotDirectoryVisible = pointer.From(props.SnapshotDirectoryVisible) + volumeGroupVolume.ThroughputInMibps = pointer.From(props.ThroughputMibps) + volumeGroupVolume.Tags = pointer.From(item.Tags) + volumeGroupVolume.ProximityPlacementGroupId = utils.NormalizeNilableString(props.ProximityPlacementGroup) + volumeGroupVolume.VolumeSpecName = pointer.From(props.VolumeSpecName) + + if props.UsageThreshold > 0 { + usageThreshold := props.UsageThreshold / 1073741824 + volumeGroupVolume.StorageQuotaInGB = usageThreshold + } + + if props.ExportPolicy != nil && props.ExportPolicy.Rules != nil && len(pointer.From(props.ExportPolicy.Rules)) > 0 { + volumeGroupVolume.ExportPolicy = flattenNetAppVolumeGroupVolumesExportPolicies(props.ExportPolicy.Rules) + } + + if props.MountTargets != nil && len(pointer.From(props.MountTargets)) > 0 { + volumeGroupVolume.MountIpAddresses = flattenNetAppVolumeGroupVolumesMountIpAddresses(props.MountTargets) + } + + // Getting volume resource directly from standalone volume + // since VolumeGroup Volumes don't return DataProtection information + volumeClient := metadata.Client.NetApp.VolumeClient + id, err := volumes.ParseVolumeID(pointer.From(item.Id)) + if err != nil { + return []NetAppVolumeGroupVolume{}, err + } + + standaloneVol, err := volumeClient.Get(ctx, pointer.From(id)) + if err != nil { + return []NetAppVolumeGroupVolume{}, fmt.Errorf("retrieving %s: %v", id, err) + } + + if standaloneVol.Model.Properties.DataProtection != nil && standaloneVol.Model.Properties.DataProtection.Replication != nil { + volumeGroupVolume.DataProtectionReplication = flattenNetAppVolumeGroupVolumesDPReplication(standaloneVol.Model.Properties.DataProtection.Replication) + } + + if standaloneVol.Model.Properties.DataProtection != nil && standaloneVol.Model.Properties.DataProtection.Snapshot != nil { + volumeGroupVolume.DataProtectionSnapshotPolicy = flattenNetAppVolumeGroupVolumesDPSnapshotPolicy(standaloneVol.Model.Properties.DataProtection.Snapshot) + } + + volumeGroupVolume.Id = pointer.From(standaloneVol.Model.Id) + + results = append(results, volumeGroupVolume) + } + + return results, nil +} + +func flattenNetAppVolumeGroupVolumesExportPolicies(input *[]volumegroups.ExportPolicyRule) []ExportPolicyRule { + results := make([]ExportPolicyRule, 0) + + if input == nil || len(pointer.From(input)) == 0 { + return results + } + + for _, item := range pointer.From(input) { + rule := ExportPolicyRule{} + + rule.RuleIndex = int(pointer.From(item.RuleIndex)) + rule.AllowedClients = pointer.From(item.AllowedClients) + rule.Nfsv3Enabled = pointer.From(item.Nfsv3) + rule.Nfsv41Enabled = pointer.From(item.Nfsv41) + rule.UnixReadOnly = pointer.From(item.UnixReadOnly) + rule.UnixReadWrite = pointer.From(item.UnixReadWrite) + rule.RootAccessEnabled = pointer.From(item.HasRootAccess) + + results = append(results, rule) + } + + return results +} + +func flattenNetAppVolumeGroupVolumesMountIpAddresses(input *[]volumegroups.MountTargetProperties) []string { + results := make([]string, 0) + + if input == nil || len(pointer.From(input)) == 0 { + return results + } + + for _, item := range pointer.From(input) { + if item.IPAddress != nil { + results = append(results, pointer.From(item.IPAddress)) + } + } + + return results +} + +func flattenNetAppVolumeGroupVolumesDPReplication(input *volumes.ReplicationObject) []DataProtectionReplication { + if input == nil { + return []DataProtectionReplication{} + } + if string(pointer.From(input.EndpointType)) == "" || !strings.EqualFold(string(pointer.From(input.EndpointType)), string(volumes.EndpointTypeDst)) { + return []DataProtectionReplication{} + } + + replicationFrequency := "" + if input.ReplicationSchedule != nil { + replicationFrequency = translateSDKSchedule(strings.ToLower(string(pointer.From(input.ReplicationSchedule)))) + } + + return []DataProtectionReplication{ + { + EndpointType: strings.ToLower(string(pointer.From(input.EndpointType))), + RemoteVolumeLocation: pointer.From(input.RemoteVolumeRegion), + RemoteVolumeResourceId: input.RemoteVolumeResourceId, + ReplicationFrequency: replicationFrequency, + }, + } +} + +func flattenNetAppVolumeGroupVolumesDPSnapshotPolicy(input *volumes.VolumeSnapshotProperties) []DataProtectionSnapshotPolicy { + if input == nil { + return []DataProtectionSnapshotPolicy{} + } + + return []DataProtectionSnapshotPolicy{ + { + DataProtectionSnapshotPolicy: pointer.From(input.SnapshotPolicyId), + }, + } +} + +func getUserDefinedVolumeName(input *string) string { + volumeName := pointer.From(input) + + if volumeName == "" { + return "" + } + + segments := len(strings.Split(volumeName, "/")) + + return strings.Split(volumeName, "/")[segments-1] +} + +func deleteVolume(ctx context.Context, metadata sdk.ResourceMetaData, volumeId string) error { + client := metadata.Client.NetApp.VolumeClient + + id, err := volumes.ParseVolumeID(volumeId) + if err != nil { + return err + } + + existing, err := client.Get(ctx, pointer.From(id)) + if err != nil { + if existing.HttpResponse.StatusCode == http.StatusNotFound { + return metadata.MarkAsGone(id) + } + return fmt.Errorf("retrieving %s: %v", id, err) + } + + // Removing replication if present + if existing.Model.Properties.DataProtection != nil && existing.Model.Properties.DataProtection.Replication != nil { + dataProtectionReplication := existing.Model.Properties.DataProtection + replicaVolumeId, err := volumesreplication.ParseVolumeID(id.ID()) + if err != nil { + return err + } + if dataProtectionReplication.Replication.EndpointType != nil && !strings.EqualFold(string(pointer.From(dataProtectionReplication.Replication.EndpointType)), string(volumes.EndpointTypeDst)) { + // This is the case where primary volume started the deletion, in this case, to be consistent we will remove replication from secondary + replicaVolumeId, err = volumesreplication.ParseVolumeID(dataProtectionReplication.Replication.RemoteVolumeResourceId) + if err != nil { + return err + } + } + + replicationClient := metadata.Client.NetApp.VolumeReplicationClient + // Checking replication status before deletion, it need to be broken before proceeding with deletion + if res, err := replicationClient.VolumesReplicationStatus(ctx, pointer.From(replicaVolumeId)); err == nil { + // Wait for replication state = "mirrored" + if model := res.Model; model != nil { + if model.MirrorState != nil && strings.ToLower(string(pointer.From(model.MirrorState))) == "uninitialized" { + if err := waitForReplMirrorState(ctx, replicationClient, pointer.From(replicaVolumeId), "mirrored"); err != nil { + return fmt.Errorf("waiting for replica %s to become 'mirrored': %+v", pointer.From(replicaVolumeId), err) + } + } + } + + // Breaking replication + if err = replicationClient.VolumesBreakReplicationThenPoll(ctx, pointer.From(replicaVolumeId), volumesreplication.BreakReplicationRequest{ + ForceBreakReplication: utils.Bool(true), + }); err != nil { + return fmt.Errorf("breaking replication for %s: %+v", pointer.From(replicaVolumeId), err) + } + + // Waiting for replication be in broken state + metadata.Logger.Infof("waiting for the replication of %s to be in broken state", pointer.From(replicaVolumeId)) + if err := waitForReplMirrorState(ctx, replicationClient, pointer.From(replicaVolumeId), "broken"); err != nil { + return fmt.Errorf("waiting for the breaking of replication for %s: %+v", pointer.From(replicaVolumeId), err) + } + } + + // Deleting replication and waiting for it to fully complete the operation + if err = replicationClient.VolumesDeleteReplicationThenPoll(ctx, pointer.From(replicaVolumeId)); err != nil { + return fmt.Errorf("deleting replicate %s: %+v", pointer.From(replicaVolumeId), err) + } + + if err := waitForReplicationDeletion(ctx, replicationClient, pointer.From(replicaVolumeId)); err != nil { + return fmt.Errorf("waiting for the replica %s to be deleted: %+v", pointer.From(replicaVolumeId), err) + } + } + + // Deleting volume and waiting for it fo fully complete the operation + if err = client.DeleteThenPoll(ctx, pointer.From(id), volumes.DeleteOperationOptions{ + ForceDelete: utils.Bool(true), + }); err != nil { + return fmt.Errorf("deleting %s: %+v", pointer.From(id), err) + } + + if err = waitForVolumeDeletion(ctx, client, pointer.From(id)); err != nil { + return fmt.Errorf("waiting for deletion of %s: %+v", pointer.From(id), err) + } + + return nil +} + +func waitForVolumeCreateOrUpdate(ctx context.Context, client *volumes.VolumesClient, id volumes.VolumeId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("context had no deadline") + } + stateConf := &pluginsdk.StateChangeConf{ + ContinuousTargetOccurence: 5, + Delay: 10 * time.Second, + MinTimeout: 10 * time.Second, + Pending: []string{"204", "404"}, + Target: []string{"200", "202"}, + Refresh: netappVolumeStateRefreshFunc(ctx, client, id), + Timeout: time.Until(deadline), + } + + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for %s to finish creating: %+v", id, err) + } + + return nil +} + +func waitForVolumeGroupCreateOrUpdate(ctx context.Context, client *volumegroups.VolumeGroupsClient, id volumegroups.VolumeGroupId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("context had no deadline") + } + stateConf := &pluginsdk.StateChangeConf{ + ContinuousTargetOccurence: 5, + Delay: 10 * time.Second, + MinTimeout: 10 * time.Second, + Pending: []string{"204", "404"}, + Target: []string{"200", "202"}, + Refresh: netappVolumeGroupStateRefreshFunc(ctx, client, id), + Timeout: time.Until(deadline), + } + + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for %s to finish creating: %+v", id, err) + } + + return nil +} + +func waitForReplAuthorization(ctx context.Context, client *volumesreplication.VolumesReplicationClient, id volumesreplication.VolumeId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("context had no deadline") + } + stateConf := &pluginsdk.StateChangeConf{ + ContinuousTargetOccurence: 5, + Delay: 10 * time.Second, + MinTimeout: 10 * time.Second, + Pending: []string{"204", "404", "400"}, // TODO: Remove 400 when bug is fixed on RP side, where replicationStatus returns 400 at some point during authorization process + Target: []string{"200", "202"}, + Refresh: netappVolumeReplicationStateRefreshFunc(ctx, client, id), + Timeout: time.Until(deadline), + } + + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for replication authorization %s to complete: %+v", id, err) + } + + return nil +} + +func waitForReplMirrorState(ctx context.Context, client *volumesreplication.VolumesReplicationClient, id volumesreplication.VolumeId, desiredState string) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("context had no deadline") + } + stateConf := &pluginsdk.StateChangeConf{ + ContinuousTargetOccurence: 5, + Delay: 10 * time.Second, + MinTimeout: 10 * time.Second, + Pending: []string{"200"}, // 200 means mirror state is still Mirrored + Target: []string{"204"}, // 204 means mirror state is <> than Mirrored + Refresh: netappVolumeReplicationMirrorStateRefreshFunc(ctx, client, id, desiredState), + Timeout: time.Until(deadline), + } + + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for %s to be in the state %q: %+v", id, desiredState, err) + } + + return nil +} + +func waitForReplicationDeletion(ctx context.Context, client *volumesreplication.VolumesReplicationClient, id volumesreplication.VolumeId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("context had no deadline") + } + + stateConf := &pluginsdk.StateChangeConf{ + ContinuousTargetOccurence: 5, + Delay: 10 * time.Second, + MinTimeout: 10 * time.Second, + Pending: []string{"200", "202", "400"}, // TODO: Remove 400 when bug is fixed on RP side, where replicationStatus returns 400 while it is in "Deleting" state + Target: []string{"404"}, + Refresh: netappVolumeReplicationStateRefreshFunc(ctx, client, id), + Timeout: time.Until(deadline), + } + + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for Replication of %s to be deleted: %+v", id, err) + } + + return nil +} + +func waitForVolumeDeletion(ctx context.Context, client *volumes.VolumesClient, id volumes.VolumeId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("context had no deadline") + } + stateConf := &pluginsdk.StateChangeConf{ + ContinuousTargetOccurence: 5, + Delay: 10 * time.Second, + MinTimeout: 10 * time.Second, + Pending: []string{"200", "202"}, + Target: []string{"204", "404"}, + Refresh: netappVolumeStateRefreshFunc(ctx, client, id), + Timeout: time.Until(deadline), + } + + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for %s to be deleted: %+v", id, err) + } + + return nil +} + +func netappVolumeStateRefreshFunc(ctx context.Context, client *volumes.VolumesClient, id volumes.VolumeId) pluginsdk.StateRefreshFunc { + return func() (interface{}, string, error) { + res, err := client.Get(ctx, id) + if err != nil { + if !response.WasNotFound(res.HttpResponse) { + return nil, "", fmt.Errorf("retrieving %s: %s", id, err) + } + } + + statusCode := "dropped connection" + if res.HttpResponse != nil { + statusCode = strconv.Itoa(res.HttpResponse.StatusCode) + } + return res, statusCode, nil + } +} + +func netappVolumeGroupStateRefreshFunc(ctx context.Context, client *volumegroups.VolumeGroupsClient, id volumegroups.VolumeGroupId) pluginsdk.StateRefreshFunc { + return func() (interface{}, string, error) { + res, err := client.VolumeGroupsGet(ctx, id) + if err != nil { + if !response.WasNotFound(res.HttpResponse) { + return nil, "", fmt.Errorf("retrieving %s: %s", id, err) + } + } + + statusCode := "dropped connection" + if res.HttpResponse != nil { + statusCode = strconv.Itoa(res.HttpResponse.StatusCode) + } + return res, statusCode, nil + } +} + +func netappVolumeReplicationMirrorStateRefreshFunc(ctx context.Context, client *volumesreplication.VolumesReplicationClient, id volumesreplication.VolumeId, desiredState string) pluginsdk.StateRefreshFunc { + validStates := []string{"mirrored", "broken", "uninitialized"} + + return func() (interface{}, string, error) { + // Possible Mirror States to be used as desiredStates: + // mirrored, broken or uninitialized + if !utils.SliceContainsValue(validStates, strings.ToLower(desiredState)) { + return nil, "", fmt.Errorf("invalid desired mirror state was passed to check mirror replication state (%s), possible values: (%+v)", desiredState, volumesreplication.PossibleValuesForMirrorState()) + } + + res, err := client.VolumesReplicationStatus(ctx, id) + if err != nil { + if !response.WasNotFound(res.HttpResponse) { + return nil, "", fmt.Errorf("retrieving replication status information from %s: %s", id, err) + } + } + + // TODO: fix this refresh function to use strings instead of fake status codes + // Setting 200 as default response + response := 200 + if res.Model != nil && res.Model.MirrorState != nil && strings.EqualFold(string(*res.Model.MirrorState), desiredState) { + // return 204 if state matches desired state + response = 204 + } + + return res, strconv.Itoa(response), nil + } +} + +func netappVolumeReplicationStateRefreshFunc(ctx context.Context, client *volumesreplication.VolumesReplicationClient, id volumesreplication.VolumeId) pluginsdk.StateRefreshFunc { + return func() (interface{}, string, error) { + res, err := client.VolumesReplicationStatus(ctx, id) + if err != nil { + if response.WasBadRequest(res.HttpResponse) && (strings.Contains(strings.ToLower(err.Error()), "deleting") || strings.Contains(strings.ToLower(err.Error()), "volume replication missing or deleted")) { + // This error can be ignored until a bug is fixed on RP side that it is returning 400 while the replication is in "Deleting" process + // TODO: remove this workaround when above bug is fixed + } else if !response.WasNotFound(res.HttpResponse) { + return nil, "", fmt.Errorf("retrieving replication status from %s: %s", id, err) + } + } + statusCode := "dropped connection" + if res.HttpResponse != nil { + statusCode = strconv.Itoa(res.HttpResponse.StatusCode) + } + return res, statusCode, nil + } +} + +func translateTFSchedule(scheduleName string) string { + if strings.EqualFold(scheduleName, string(ReplicationSchedule10Minutes)) { + return string(volumegroups.ReplicationScheduleOneZerominutely) + } + + return scheduleName +} + +func translateSDKSchedule(scheduleName string) string { + if strings.EqualFold(scheduleName, string(volumegroups.ReplicationScheduleOneZerominutely)) { + return string(ReplicationSchedule10Minutes) + } + + return scheduleName +} diff --git a/internal/services/netapp/netapp_volume_resource.go b/internal/services/netapp/netapp_volume_resource.go index b3b943b73e2e..49ba0e12e478 100644 --- a/internal/services/netapp/netapp_volume_resource.go +++ b/internal/services/netapp/netapp_volume_resource.go @@ -1,10 +1,8 @@ package netapp import ( - "context" "fmt" "log" - "strconv" "strings" "time" @@ -47,6 +45,8 @@ func resourceNetAppVolume() *pluginsdk.Resource { }), Schema: map[string]*pluginsdk.Schema{ + "resource_group_name": commonschema.ResourceGroupName(), + "name": { Type: pluginsdk.TypeString, Required: true, @@ -54,8 +54,6 @@ func resourceNetAppVolume() *pluginsdk.Resource { ValidateFunc: netAppValidate.VolumeName, }, - "resource_group_name": commonschema.ResourceGroupName(), - "location": commonschema.Location(), "zone": commonschema.ZoneSingleOptionalForceNew(), @@ -720,182 +718,6 @@ func resourceNetAppVolumeDelete(d *pluginsdk.ResourceData, meta interface{}) err return nil } -func waitForVolumeCreateOrUpdate(ctx context.Context, client *volumes.VolumesClient, id volumes.VolumeId) error { - deadline, ok := ctx.Deadline() - if !ok { - return fmt.Errorf("context had no deadline") - } - stateConf := &pluginsdk.StateChangeConf{ - ContinuousTargetOccurence: 5, - Delay: 10 * time.Second, - MinTimeout: 10 * time.Second, - Pending: []string{"204", "404"}, - Target: []string{"200", "202"}, - Refresh: netappVolumeStateRefreshFunc(ctx, client, id), - Timeout: time.Until(deadline), - } - - if _, err := stateConf.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting for %s to finish creating: %+v", id, err) - } - - return nil -} - -func waitForReplAuthorization(ctx context.Context, client *volumesreplication.VolumesReplicationClient, id volumesreplication.VolumeId) error { - deadline, ok := ctx.Deadline() - if !ok { - return fmt.Errorf("context had no deadline") - } - stateConf := &pluginsdk.StateChangeConf{ - ContinuousTargetOccurence: 5, - Delay: 10 * time.Second, - MinTimeout: 10 * time.Second, - Pending: []string{"204", "404", "400"}, // TODO: Remove 400 when bug is fixed on RP side, where replicationStatus returns 400 at some point during authorization process - Target: []string{"200", "202"}, - Refresh: netappVolumeReplicationStateRefreshFunc(ctx, client, id), - Timeout: time.Until(deadline), - } - - if _, err := stateConf.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting for replication authorization %s to complete: %+v", id, err) - } - - return nil -} - -func waitForReplMirrorState(ctx context.Context, client *volumesreplication.VolumesReplicationClient, id volumesreplication.VolumeId, desiredState string) error { - deadline, ok := ctx.Deadline() - if !ok { - return fmt.Errorf("context had no deadline") - } - stateConf := &pluginsdk.StateChangeConf{ - ContinuousTargetOccurence: 5, - Delay: 10 * time.Second, - MinTimeout: 10 * time.Second, - Pending: []string{"200"}, // 200 means mirror state is still Mirrored - Target: []string{"204"}, // 204 means mirror state is <> than Mirrored - Refresh: netappVolumeReplicationMirrorStateRefreshFunc(ctx, client, id, desiredState), - Timeout: time.Until(deadline), - } - - if _, err := stateConf.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting for %s to be in the state %q: %+v", id, desiredState, err) - } - - return nil -} - -func waitForReplicationDeletion(ctx context.Context, client *volumesreplication.VolumesReplicationClient, id volumesreplication.VolumeId) error { - deadline, ok := ctx.Deadline() - if !ok { - return fmt.Errorf("context had no deadline") - } - - stateConf := &pluginsdk.StateChangeConf{ - ContinuousTargetOccurence: 5, - Delay: 10 * time.Second, - MinTimeout: 10 * time.Second, - Pending: []string{"200", "202", "400"}, // TODO: Remove 400 when bug is fixed on RP side, where replicationStatus returns 400 while it is in "Deleting" state - Target: []string{"404"}, - Refresh: netappVolumeReplicationStateRefreshFunc(ctx, client, id), - Timeout: time.Until(deadline), - } - - if _, err := stateConf.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting for Replication of %s to be deleted: %+v", id, err) - } - - return nil -} - -func waitForVolumeDeletion(ctx context.Context, client *volumes.VolumesClient, id volumes.VolumeId) error { - deadline, ok := ctx.Deadline() - if !ok { - return fmt.Errorf("context had no deadline") - } - stateConf := &pluginsdk.StateChangeConf{ - ContinuousTargetOccurence: 5, - Delay: 10 * time.Second, - MinTimeout: 10 * time.Second, - Pending: []string{"200", "202"}, - Target: []string{"204", "404"}, - Refresh: netappVolumeStateRefreshFunc(ctx, client, id), - Timeout: time.Until(deadline), - } - - if _, err := stateConf.WaitForStateContext(ctx); err != nil { - return fmt.Errorf("waiting for %s to be deleted: %+v", id, err) - } - - return nil -} - -func netappVolumeStateRefreshFunc(ctx context.Context, client *volumes.VolumesClient, id volumes.VolumeId) pluginsdk.StateRefreshFunc { - return func() (interface{}, string, error) { - res, err := client.Get(ctx, id) - if err != nil { - if !response.WasNotFound(res.HttpResponse) { - return nil, "", fmt.Errorf("retrieving %s: %s", id, err) - } - } - - statusCode := "dropped connection" - if res.HttpResponse != nil { - statusCode = strconv.Itoa(res.HttpResponse.StatusCode) - } - return res, statusCode, nil - } -} - -func netappVolumeReplicationMirrorStateRefreshFunc(ctx context.Context, client *volumesreplication.VolumesReplicationClient, id volumesreplication.VolumeId, desiredState string) pluginsdk.StateRefreshFunc { - validStates := []string{"mirrored", "broken", "uninitialized"} - - return func() (interface{}, string, error) { - // Possible Mirror States to be used as desiredStates: - // mirrored, broken or uninitialized - if !utils.SliceContainsValue(validStates, strings.ToLower(desiredState)) { - return nil, "", fmt.Errorf("Invalid desired mirror state was passed to check mirror replication state (%s), possible values: (%+v)", desiredState, volumesreplication.PossibleValuesForMirrorState()) - } - - res, err := client.VolumesReplicationStatus(ctx, id) - if err != nil { - if !response.WasNotFound(res.HttpResponse) { - return nil, "", fmt.Errorf("retrieving replication status information from %s: %s", id, err) - } - } - - // TODO: fix this refresh function to use strings instead of fake status codes - // Setting 200 as default response - response := 200 - if res.Model != nil && res.Model.MirrorState != nil && strings.EqualFold(string(*res.Model.MirrorState), desiredState) { - // return 204 if state matches desired state - response = 204 - } - - return res, strconv.Itoa(response), nil - } -} - -func netappVolumeReplicationStateRefreshFunc(ctx context.Context, client *volumesreplication.VolumesReplicationClient, id volumesreplication.VolumeId) pluginsdk.StateRefreshFunc { - return func() (interface{}, string, error) { - res, err := client.VolumesReplicationStatus(ctx, id) - if err != nil { - if response.WasBadRequest(res.HttpResponse) && (strings.Contains(strings.ToLower(err.Error()), "deleting") || strings.Contains(strings.ToLower(err.Error()), "volume replication missing or deleted")) { - // This error can be ignored until a bug is fixed on RP side that it is returning 400 while the replication is in "Deleting" process - // TODO: remove this workaround when above bug is fixed - } else if !response.WasNotFound(res.HttpResponse) { - return nil, "", fmt.Errorf("retrieving replication status from %s: %s", id, err) - } - } - statusCode := "dropped connection" - if res.HttpResponse != nil { - statusCode = strconv.Itoa(res.HttpResponse.StatusCode) - } - return res, statusCode, nil - } -} - func expandNetAppVolumeExportPolicyRule(input []interface{}) *volumes.VolumePropertiesExportPolicy { results := make([]volumes.ExportPolicyRule, 0) for _, item := range input { @@ -958,9 +780,9 @@ func expandNetAppVolumeExportPolicyRulePatch(input []interface{}) *volumes.Volum ruleIndex := int64(v["rule_index"].(int)) allowedClients := strings.Join(*utils.ExpandStringSlice(v["allowed_clients"].(*pluginsdk.Set).List()), ",") - cifsEnabled := false nfsv3Enabled := false nfsv41Enabled := false + cifsEnabled := false if vpe := v["protocols_enabled"]; vpe != nil { protocolsEnabled := vpe.([]interface{}) @@ -1004,71 +826,6 @@ func expandNetAppVolumeExportPolicyRulePatch(input []interface{}) *volumes.Volum } } -func expandNetAppVolumeDataProtectionReplication(input []interface{}) *volumes.VolumePropertiesDataProtection { - if len(input) == 0 || input[0] == nil { - return &volumes.VolumePropertiesDataProtection{} - } - - replicationObject := volumes.ReplicationObject{} - - replicationRaw := input[0].(map[string]interface{}) - - if v, ok := replicationRaw["endpoint_type"]; ok { - endpointType := volumes.EndpointType(v.(string)) - replicationObject.EndpointType = &endpointType - } - if v, ok := replicationRaw["remote_volume_location"]; ok { - replicationObject.RemoteVolumeRegion = utils.String(v.(string)) - } - if v, ok := replicationRaw["remote_volume_resource_id"]; ok { - replicationObject.RemoteVolumeResourceId = v.(string) - } - if v, ok := replicationRaw["replication_frequency"]; ok { - replicationSchedule := volumes.ReplicationSchedule(translateTFSchedule(v.(string))) - replicationObject.ReplicationSchedule = &replicationSchedule - } - - return &volumes.VolumePropertiesDataProtection{ - Replication: &replicationObject, - } -} - -func expandNetAppVolumeDataProtectionSnapshotPolicy(input []interface{}) *volumes.VolumePropertiesDataProtection { - if len(input) == 0 || input[0] == nil { - return &volumes.VolumePropertiesDataProtection{} - } - - snapshotObject := volumes.VolumeSnapshotProperties{} - - snapshotRaw := input[0].(map[string]interface{}) - - if v, ok := snapshotRaw["snapshot_policy_id"]; ok { - snapshotObject.SnapshotPolicyId = utils.String(v.(string)) - } - - return &volumes.VolumePropertiesDataProtection{ - Snapshot: &snapshotObject, - } -} - -func expandNetAppVolumeDataProtectionSnapshotPolicyPatch(input []interface{}) *volumes.VolumePatchPropertiesDataProtection { - if len(input) == 0 || input[0] == nil { - return &volumes.VolumePatchPropertiesDataProtection{} - } - - snapshotObject := volumes.VolumeSnapshotProperties{} - - snapshotRaw := input[0].(map[string]interface{}) - - if v, ok := snapshotRaw["snapshot_policy_id"]; ok { - snapshotObject.SnapshotPolicyId = utils.String(v.(string)) - } - - return &volumes.VolumePatchPropertiesDataProtection{ - Snapshot: &snapshotObject, - } -} - func flattenNetAppVolumeExportPolicyRule(input *volumes.VolumePropertiesExportPolicy) []interface{} { results := make([]interface{}, 0) if input == nil || input.Rules == nil { @@ -1178,19 +935,3 @@ func flattenNetAppVolumeDataProtectionSnapshotPolicy(input *volumes.VolumeProper }, } } - -func translateTFSchedule(scheduleName string) string { - if strings.EqualFold(scheduleName, "10minutes") { - return "_10minutely" - } - - return scheduleName -} - -func translateSDKSchedule(scheduleName string) string { - if strings.EqualFold(scheduleName, "_10minutely") { - return "10minutes" - } - - return scheduleName -} diff --git a/internal/services/netapp/netapp_volume_resource_test.go b/internal/services/netapp/netapp_volume_resource_test.go index da1ce49dd80b..6651544eb4da 100644 --- a/internal/services/netapp/netapp_volume_resource_test.go +++ b/internal/services/netapp/netapp_volume_resource_test.go @@ -942,6 +942,12 @@ provider "azurerm" { resource "azurerm_resource_group" "test" { name = "acctestRG-netapp-%d" location = "%s" + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true", + "SkipNRMSNSG" = "true" + } } resource "azurerm_virtual_network" "test" { diff --git a/internal/services/netapp/registration.go b/internal/services/netapp/registration.go index 2dedd47759d7..0d6c2f770ae1 100644 --- a/internal/services/netapp/registration.go +++ b/internal/services/netapp/registration.go @@ -7,25 +7,25 @@ import ( type Registration struct{} -var _ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{} +var ( + _ sdk.TypedServiceRegistration = Registration{} + _ sdk.UntypedServiceRegistrationWithAGitHubLabel = Registration{} +) func (r Registration) AssociatedGitHubLabel() string { return "service/netapp" } -// Name is the name of this Service func (r Registration) Name() string { return "NetApp" } -// WebsiteCategories returns a list of categories which can be used for the sidebar func (r Registration) WebsiteCategories() []string { return []string{ "NetApp", } } -// SupportedDataSources returns the supported Data Sources supported by this Service func (r Registration) SupportedDataSources() map[string]*pluginsdk.Resource { return map[string]*pluginsdk.Resource{ "azurerm_netapp_account": dataSourceNetAppAccount(), @@ -36,7 +36,6 @@ func (r Registration) SupportedDataSources() map[string]*pluginsdk.Resource { } } -// SupportedResources returns the supported Resources supported by this Service func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { return map[string]*pluginsdk.Resource{ "azurerm_netapp_account": resourceNetAppAccount(), @@ -46,3 +45,17 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { "azurerm_netapp_snapshot_policy": resourceNetAppSnapshotPolicy(), } } + +// DataSources returns the typed DataSources supported by this service +func (r Registration) DataSources() []sdk.DataSource { + return []sdk.DataSource{ + NetAppVolumeGroupSapHanaDataSource{}, + } +} + +// Resources returns the typed Resources supported by this service +func (r Registration) Resources() []sdk.Resource { + return []sdk.Resource{ + NetAppVolumeGroupSapHanaResource{}, + } +} diff --git a/internal/services/netapp/validate/volume_group_name_test.go b/internal/services/netapp/validate/volume_group_name_test.go new file mode 100644 index 000000000000..1148a5e916a8 --- /dev/null +++ b/internal/services/netapp/validate/volume_group_name_test.go @@ -0,0 +1,71 @@ +package validate + +import "testing" + +func TestNetAppVolumeGroupName(t *testing.T) { + testData := []struct { + input string + expected bool + }{ + { + // empty + input: "", + expected: false, + }, + { + // basic example + input: "hello", + expected: true, + }, + { + // can't start with an underscore + input: "_hello", + expected: false, + }, + { + // can't end with a dash + input: "hello-", + expected: true, + }, + { + // can't contain an exclamation mark + input: "hello!", + expected: false, + }, + { + // dash in the middle + input: "malcolm-in-the-middle", + expected: true, + }, + { + // can't end with a period + input: "hello.", + expected: false, + }, + { + // 63 chars + input: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk", + expected: true, + }, + { + // 64 chars + input: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkj", + expected: true, + }, + { + // 65 chars + input: "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkja", + expected: false, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q..", v.input) + + _, errors := VolumeGroupName(v.input, "name") + actual := len(errors) == 0 + if v.expected != actual { + t.Fatalf("Expected %t but got %t", v.expected, actual) + } + } +} diff --git a/internal/services/netapp/validate/volume_group_sap_hana_volumes_export_policy_validation.go b/internal/services/netapp/validate/volume_group_sap_hana_volumes_export_policy_validation.go new file mode 100644 index 000000000000..a27dcddf4663 --- /dev/null +++ b/internal/services/netapp/validate/volume_group_sap_hana_volumes_export_policy_validation.go @@ -0,0 +1,35 @@ +package validate + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups" +) + +func ValidateNetAppVolumeGroupExportPolicyRuleSAPHanna(rule volumegroups.ExportPolicyRule, protocolType string) []error { + errors := make([]error, 0) + + // Validating that nfsv3 and nfsv4.1 are not enabled in the same rule + if pointer.From(rule.Nfsv3) && pointer.From(rule.Nfsv41) { + errors = append(errors, fmt.Errorf("'nfsv3 and nfsv4.1 cannot be enabled at the same time'")) + } + + // Validating that nfsv3 and nfsv4.1 are not disabled in the same rule + if !pointer.From(rule.Nfsv3) && !pointer.From(rule.Nfsv41) { + errors = append(errors, fmt.Errorf("'nfsv3 and nfsv4.1 cannot be enabled at the same time'")) + } + + // Validating that nfsv4.1 export policy is not set on nfsv3 volume + if pointer.From(rule.Nfsv41) && strings.EqualFold(protocolType, string(ProtocolTypeNfsV3)) { + errors = append(errors, fmt.Errorf("'nfsv4.1 export policy cannot be enabled on nfsv3 volume'")) + } + + // Validating that nfsv3 export policy is not set on nfsv4.1 volume + if pointer.From(rule.Nfsv3) && strings.EqualFold(protocolType, string(ProtocolTypeNfsV41)) { + errors = append(errors, fmt.Errorf("'nfsv3 export policy cannot be enabled on nfsv4.1 volume'")) + } + + return errors +} diff --git a/internal/services/netapp/validate/volume_group_sap_hana_volumes_export_policy_validation_test.go b/internal/services/netapp/validate/volume_group_sap_hana_volumes_export_policy_validation_test.go new file mode 100644 index 000000000000..03ed00c85072 --- /dev/null +++ b/internal/services/netapp/validate/volume_group_sap_hana_volumes_export_policy_validation_test.go @@ -0,0 +1,101 @@ +package validate + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func TestValidateNetAppVolumeGroupExportPolicyRuleSAPHanna(t *testing.T) { + cases := []struct { + Name string + Protocol string + Rule volumegroups.ExportPolicyRule + Errors int + }{ + { + Name: "ValidateNFSv41EnabledOnNFSv41Volume", + Protocol: string(ProtocolTypeNfsV41), + Rule: volumegroups.ExportPolicyRule{ + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + Errors: 0, + }, + { + Name: "ValidateNFSv3EnabledOnNFSv3Volume", + Protocol: string(ProtocolTypeNfsV3), + Rule: volumegroups.ExportPolicyRule{ + Nfsv3: pointer.To(true), + Nfsv41: utils.Bool(false), + }, + Errors: 0, + }, + { + Name: "ValidateBothProtocolsNotEnabledAtSameTimeOnNFSv41Volume", + Protocol: string(ProtocolTypeNfsV41), + Rule: volumegroups.ExportPolicyRule{ + Nfsv3: pointer.To(true), + Nfsv41: utils.Bool(true), + }, + Errors: 2, + }, + { + Name: "ValidateBothProtocolsNotEnabledAtSameTimeOnNFSv3Volume", + Protocol: string(ProtocolTypeNfsV3), + Rule: volumegroups.ExportPolicyRule{ + Nfsv3: pointer.To(true), + Nfsv41: utils.Bool(true), + }, + Errors: 2, + }, + { + Name: "ValidateBothProtocolsNotDisabledAtSameTimeOnNFSv3Volume", + Protocol: string(ProtocolTypeNfsV3), + Rule: volumegroups.ExportPolicyRule{ + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(false), + }, + Errors: 1, + }, + { + Name: "ValidateBothProtocolsNotDisabledAtSameTimeOnNFSv41Volume", + Protocol: string(ProtocolTypeNfsV41), + Rule: volumegroups.ExportPolicyRule{ + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(false), + }, + Errors: 1, + }, + { + Name: "ValidateNFSv3NotEnabledOnNFSv41Volume", + Protocol: string(ProtocolTypeNfsV41), + Rule: volumegroups.ExportPolicyRule{ + Nfsv3: pointer.To(true), + Nfsv41: utils.Bool(false), + }, + Errors: 1, + }, + { + Name: "ValidateNFSv41NotEnabledOnNFSv3Volume", + Protocol: string(ProtocolTypeNfsV3), + Rule: volumegroups.ExportPolicyRule{ + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + Errors: 1, + }, + } + + for _, tc := range cases { + t.Run(tc.Name, func(t *testing.T) { + errors := ValidateNetAppVolumeGroupExportPolicyRuleSAPHanna(tc.Rule, tc.Protocol) + + if len(errors) != tc.Errors { + t.Fatalf("expected ValidateNetAppVolumeGroupSAPHanaVolumes to return %d error(s) not %d", tc.Errors, len(errors)) + } + }) + } +} diff --git a/internal/services/netapp/validate/volume_group_sap_hana_volumes_validation.go b/internal/services/netapp/validate/volume_group_sap_hana_volumes_validation.go new file mode 100644 index 000000000000..deb64458faf1 --- /dev/null +++ b/internal/services/netapp/validate/volume_group_sap_hana_volumes_validation.go @@ -0,0 +1,199 @@ +package validate + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type VolumeSpecNameSapHana string + +const ( + VolumeSpecNameSapHanaData VolumeSpecNameSapHana = "data" + VolumeSpecNameSapHanaLog VolumeSpecNameSapHana = "log" + VolumeSpecNameSapHanaShared VolumeSpecNameSapHana = "shared" + VolumeSpecNameSapHanaDataBackup VolumeSpecNameSapHana = "data-backup" + VolumeSpecNameSapHanaLogBackup VolumeSpecNameSapHana = "log-backup" +) + +func PossibleValuesForVolumeSpecNameSapHana() []string { + return []string{ + string(VolumeSpecNameSapHanaData), + string(VolumeSpecNameSapHanaLog), + string(VolumeSpecNameSapHanaShared), + string(VolumeSpecNameSapHanaDataBackup), + string(VolumeSpecNameSapHanaLogBackup), + } +} + +func RequiredVolumesForSAPHANA() []string { + return []string{ + string(VolumeSpecNameSapHanaData), + string(VolumeSpecNameSapHanaLog), + } +} + +type ProtocolType string + +const ( + ProtocolTypeNfsV41 ProtocolType = "NFSv4.1" + ProtocolTypeNfsV3 ProtocolType = "NFSv3" + ProtocolTypeCifs ProtocolType = "CIFS" +) + +func PossibleValuesForProtocolType() []string { + return []string{ + string(ProtocolTypeNfsV41), + string(ProtocolTypeNfsV3), + string(ProtocolTypeCifs), + } +} + +func PossibleValuesForProtocolTypeVolumeGroupSapHana() []string { + return []string{ + string(ProtocolTypeNfsV41), + string(ProtocolTypeNfsV3), + } +} + +// Diverging from the SDK volumegroups.SecurityStyle since it is defined as lower case +// but the backend changes it to Pascal case on GET. Please refer to https://github.com/Azure/azure-sdk-for-go/issues/14684 +type SecurityStyle string + +const ( + SecurityStyleUnix SecurityStyle = "Unix" + SecurityStyleNtfs SecurityStyle = "Ntfs" +) + +func PossibleValuesForSecurityStyle() []string { + return []string{ + string(SecurityStyleUnix), + } +} + +func ValidateNetAppVolumeGroupSAPHanaVolumes(volumeList *[]volumegroups.VolumeGroupVolumeProperties) []error { + errors := make([]error, 0) + volumeSpecRepeatCount := make(map[string]int) + applicationType := string(volumegroups.ApplicationTypeSAPNegativeHANA) + + // Validating minimum volume count + if len(*volumeList) < len(RequiredVolumesForSAPHANA()) { + errors = append(errors, fmt.Errorf("'minimum %v volumes are required for %v'", len(RequiredVolumesForSAPHANA()), applicationType)) + } + + // Validating each volume + for _, volume := range pointer.From(volumeList) { + + // Get protocol list + protocolTypeList := pointer.From(volume.Properties.ProtocolTypes) + protocolType := "" + + // Validate protocol list is not empty + if len(protocolTypeList) == 0 { + errors = append(errors, fmt.Errorf("'protocol type list cannot be empty'")) + } + + // Validate protocol list is not > 1 + if len(protocolTypeList) > 1 { + errors = append(errors, fmt.Errorf("'multi-protocol volumes are not supported, protocol count is %v'", len(protocolTypeList))) + } + + // Getting protocol for next validations + if len(protocolTypeList) > 0 { + protocolType = protocolTypeList[0] + } + + // Validate protocol list does not contain invalid protocols + for _, protocol := range protocolTypeList { + if !findStringInSlice(PossibleValuesForProtocolType(), protocolType) { + errors = append(errors, fmt.Errorf("'protocol %v is invalid'", protocol)) + } + } + + // Validate that protocol is valid for SAP Hana + if !findStringInSlice(PossibleValuesForProtocolTypeVolumeGroupSapHana(), protocolType) { + errors = append(errors, fmt.Errorf("'protocol %v is invalid for SAP Hana'", protocolType)) + } + + // Can't be nfsv3 on data, log and share volumes + if strings.EqualFold(protocolType, string(ProtocolTypeNfsV3)) && + (strings.EqualFold(pointer.From(volume.Properties.VolumeSpecName), string(VolumeSpecNameSapHanaData)) || + strings.EqualFold(pointer.From(volume.Properties.VolumeSpecName), string(VolumeSpecNameSapHanaShared)) || + strings.EqualFold(pointer.From(volume.Properties.VolumeSpecName), string(VolumeSpecNameSapHanaLog))) { + + errors = append(errors, fmt.Errorf("'nfsv3 on data, log and shared volumes for %v is not supported on volume %v'", applicationType, pointer.From(volume.Name))) + } + + // Validating export policies + if volume.Properties.ExportPolicy != nil { + for _, rule := range pointer.From(volume.Properties.ExportPolicy.Rules) { + errors = append(errors, ValidateNetAppVolumeGroupExportPolicyRuleSAPHanna(rule, protocolType)...) + } + } + + // Checking CRR rule that log cannot be DataProtection type + if strings.EqualFold(pointer.From(volume.Properties.VolumeSpecName), string(VolumeSpecNameSapHanaLog)) && + volume.Properties.DataProtection != nil && + volume.Properties.DataProtection.Replication != nil && + strings.EqualFold(string(pointer.From(volume.Properties.DataProtection.Replication.EndpointType)), string(volumegroups.EndpointTypeDst)) { + + errors = append(errors, fmt.Errorf("'log volume spec type cannot be DataProtection type for %v on volume %v'", applicationType, pointer.From(volume.Name))) + } + + // Validating that snapshot policies are not being created in a data protection volume + if volume.Properties.DataProtection != nil && + volume.Properties.DataProtection.Snapshot != nil && + (volume.Properties.DataProtection.Replication != nil && strings.EqualFold(string(pointer.From(volume.Properties.DataProtection.Replication.EndpointType)), string(volumegroups.EndpointTypeDst))) { + + errors = append(errors, fmt.Errorf("'snapshot policy cannot be enabled on a data protection volume for %v on volume %v'", applicationType, pointer.From(volume.Name))) + } + + // Validating that data-backup and log-backup don't have PPG defined + if (strings.EqualFold(pointer.From(volume.Properties.VolumeSpecName), string(VolumeSpecNameSapHanaDataBackup)) || + strings.EqualFold(pointer.From(volume.Properties.VolumeSpecName), string(VolumeSpecNameSapHanaLogBackup))) && + utils.NormalizeNilableString(volume.Properties.ProximityPlacementGroup) != "" { + + errors = append(errors, fmt.Errorf("'%v volume spec type cannot have PPG defined for %v on volume %v'", pointer.From(volume.Properties.VolumeSpecName), applicationType, pointer.From(volume.Name))) + } + + // Validating that data, log and shared have PPG defined. + if (strings.EqualFold(pointer.From(volume.Properties.VolumeSpecName), string(VolumeSpecNameSapHanaData)) || + strings.EqualFold(pointer.From(volume.Properties.VolumeSpecName), string(VolumeSpecNameSapHanaLog)) || + strings.EqualFold(pointer.From(volume.Properties.VolumeSpecName), string(VolumeSpecNameSapHanaShared))) && + utils.NormalizeNilableString(volume.Properties.ProximityPlacementGroup) == "" { + + errors = append(errors, fmt.Errorf("'%v volume spec type must have PPG defined for %v on volume %v'", pointer.From(volume.Properties.VolumeSpecName), applicationType, pointer.From(volume.Name))) + } + + // Adding volume spec name to hashmap for post volume loop check + volumeSpecRepeatCount[pointer.From(volume.Properties.VolumeSpecName)] += 1 + } + + // Validating required volume spec types + for _, requiredVolumeSpec := range RequiredVolumesForSAPHANA() { + if _, ok := volumeSpecRepeatCount[requiredVolumeSpec]; !ok { + errors = append(errors, fmt.Errorf("'required volume spec type %v is not present for %v'", requiredVolumeSpec, applicationType)) + } + } + + // Validating that volume spec does not repeat + for volumeSpecName, count := range volumeSpecRepeatCount { + if count > 1 { + errors = append(errors, fmt.Errorf("'volume spec type %v cannot be repeated for %v'", volumeSpecName, applicationType)) + } + } + + return errors +} + +func findStringInSlice(slice []string, val string) bool { + for _, item := range slice { + if strings.EqualFold(item, val) { + return true + } + } + return false +} diff --git a/internal/services/netapp/validate/volume_group_sap_hana_volumes_validation_test.go b/internal/services/netapp/validate/volume_group_sap_hana_volumes_validation_test.go new file mode 100644 index 000000000000..3a1a2494fdc6 --- /dev/null +++ b/internal/services/netapp/validate/volume_group_sap_hana_volumes_validation_test.go @@ -0,0 +1,545 @@ +package validate + +import ( + "fmt" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func TestValidateNetAppVolumeGroupSAPHanaVolumes(t *testing.T) { + cases := []struct { + Name string + VolumesData []volumegroups.VolumeGroupVolumeProperties + Errors int + }{ + { + Name: "ValidateCorrectSettingsAllVolumes", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLog))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLog)), + }, + }, + { // shared + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaShared))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaShared)), + }, + }, + { // data-backup + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaDataBackup))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaDataBackup)), + }, + }, + { // log-backup + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLogBackup))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLogBackup)), + }, + }, + }, + Errors: 0, + }, + { + Name: "ValidateMinimumVolumes", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLog))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLog)), + }, + }, + }, + Errors: 0, + }, + { + Name: "ValidateRequiredVolumeSpecs", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // shared + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaShared))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaShared)), + }, + }, + { // data-backup + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaDataBackup))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaDataBackup)), + }, + }, + { // log-backup + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLogBackup))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLogBackup)), + }, + }, + }, + Errors: 2, + }, + { + Name: "ValidateLessThanMinimumVolumes", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + }, + }, + }, + Errors: 2, + }, + { + Name: "ValidateMultiProtocolFails", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1", "NFSv3"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + }, + }, + }, + Errors: 3, + }, + { + Name: "ValidateNoProtocolFails", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + }, + }, + }, + Errors: 4, + }, + { + Name: "ValidateInvalidProtocolList", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1", "InvalidProtocol"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + }, + }, + }, + Errors: 3, + }, + { + Name: "ValidateInvalidProtocol", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"InvalidProtocol"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + }, + }, + }, + Errors: 4, + }, + { + Name: "ValidateCIFSInvalidProtocolForSAPHana", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: utils.Bool(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"CIFS"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + }, + }, + }, + Errors: 3, + }, + { + Name: "ValidateNoNfsVersionThreeOnDataLogAndSharedVolumes", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv3"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLog))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv3"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLog)), + }, + }, + { // shared + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaShared))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv3"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaShared)), + }, + }, + }, + Errors: 3, + }, + { + Name: "ValidateNoPPGBackupVolumes", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLog))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLog)), + }, + }, + { // data-backup + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaDataBackup))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaDataBackup)), + }, + }, + { // log-backup + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLogBackup))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLogBackup)), + }, + }, + }, + Errors: 2, + }, + { + Name: "ValidateRequiredPpgForNonBackupVolumes", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLog))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLog)), + }, + }, + { // shared + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaShared))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaShared)), + }, + }, + }, + Errors: 3, + }, + { + Name: "ValidateVolumeSpecCantRepeat", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLog))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLog)), + }, + }, + { // shared + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaShared))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + }, + }, + }, + Errors: 1, + }, + { + Name: "ValidateEndpointDstNotEnabledOnLogVolume", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLog))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLog)), + DataProtection: &volumegroups.VolumePropertiesDataProtection{ + Replication: &volumegroups.ReplicationObject{ + EndpointType: pointer.To(volumegroups.EndpointTypeDst), + }, + }, + }, + }, + }, + Errors: 1, + }, + { + Name: "ValidateSnapshotPolicyNotEnabledOnEndpointDstVolume", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + DataProtection: &volumegroups.VolumePropertiesDataProtection{ + Replication: &volumegroups.ReplicationObject{ + EndpointType: pointer.To(volumegroups.EndpointTypeDst), + }, + Snapshot: &volumegroups.VolumeSnapshotProperties{ + SnapshotPolicyId: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.NetApp/netAppAccounts/account1/capacityPools/pool1/volumes/volume1/snapshotPolicies/snapshotPolicy1"), + }, + }, + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLog))), + Properties: volumegroups.VolumeProperties{ + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(SecurityStyleUnix)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLog)), + }, + }, + }, + Errors: 1, + }, + } + + for _, tc := range cases { + t.Run(tc.Name, func(t *testing.T) { + errors := ValidateNetAppVolumeGroupSAPHanaVolumes(pointer.To(tc.VolumesData)) + + if len(errors) != tc.Errors { + t.Fatalf("expected ValidateNetAppVolumeGroupSAPHanaVolumes to return %d error(s) not %d\nError List: \n%v", tc.Errors, len(errors), errors) + } + }) + } +} diff --git a/internal/services/netapp/validate/volume_grupo_name.go b/internal/services/netapp/validate/volume_grupo_name.go new file mode 100644 index 000000000000..b0e4cea86ec0 --- /dev/null +++ b/internal/services/netapp/validate/volume_grupo_name.go @@ -0,0 +1,16 @@ +package validate + +import ( + "fmt" + "regexp" +) + +func VolumeGroupName(v interface{}, k string) (warnings []string, errors []error) { + value := v.(string) + + if !regexp.MustCompile(`^[a-zA-Z][-_\da-zA-Z]{0,63}$`).MatchString(value) { + errors = append(errors, fmt.Errorf("%q must be between 1 and 64 characters in length and start with letters and contains only letters, numbers, underscore or hyphens.", k)) + } + + return warnings, errors +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/README.md b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/README.md new file mode 100644 index 000000000000..bd321b7befaf --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/README.md @@ -0,0 +1,81 @@ + +## `github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups` Documentation + +The `volumegroups` SDK allows for interaction with the Azure Resource Manager Service `netapp` (API Version `2022-05-01`). + +This readme covers example usages, but further information on [using this SDK can be found in the project root](https://github.com/hashicorp/go-azure-sdk/tree/main/docs). + +### Import Path + +```go +import "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups" +``` + + +### Client Initialization + +```go +client := volumegroups.NewVolumeGroupsClientWithBaseURI("https://management.azure.com") +client.Client.Authorizer = authorizer +``` + + +### Example Usage: `VolumeGroupsClient.VolumeGroupsCreate` + +```go +ctx := context.TODO() +id := volumegroups.NewVolumeGroupID("12345678-1234-9876-4563-123456789012", "example-resource-group", "netAppAccountValue", "volumeGroupValue") + +payload := volumegroups.VolumeGroupDetails{ + // ... +} + + +if err := client.VolumeGroupsCreateThenPoll(ctx, id, payload); err != nil { + // handle the error +} +``` + + +### Example Usage: `VolumeGroupsClient.VolumeGroupsDelete` + +```go +ctx := context.TODO() +id := volumegroups.NewVolumeGroupID("12345678-1234-9876-4563-123456789012", "example-resource-group", "netAppAccountValue", "volumeGroupValue") + +if err := client.VolumeGroupsDeleteThenPoll(ctx, id); err != nil { + // handle the error +} +``` + + +### Example Usage: `VolumeGroupsClient.VolumeGroupsGet` + +```go +ctx := context.TODO() +id := volumegroups.NewVolumeGroupID("12345678-1234-9876-4563-123456789012", "example-resource-group", "netAppAccountValue", "volumeGroupValue") + +read, err := client.VolumeGroupsGet(ctx, id) +if err != nil { + // handle the error +} +if model := read.Model; model != nil { + // do something with the model/response object +} +``` + + +### Example Usage: `VolumeGroupsClient.VolumeGroupsListByNetAppAccount` + +```go +ctx := context.TODO() +id := volumegroups.NewNetAppAccountID("12345678-1234-9876-4563-123456789012", "example-resource-group", "netAppAccountValue") + +read, err := client.VolumeGroupsListByNetAppAccount(ctx, id) +if err != nil { + // handle the error +} +if model := read.Model; model != nil { + // do something with the model/response object +} +``` diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/client.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/client.go new file mode 100644 index 000000000000..c7beab84b9a1 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/client.go @@ -0,0 +1,18 @@ +package volumegroups + +import "github.com/Azure/go-autorest/autorest" + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumeGroupsClient struct { + Client autorest.Client + baseUri string +} + +func NewVolumeGroupsClientWithBaseURI(endpoint string) VolumeGroupsClient { + return VolumeGroupsClient{ + Client: autorest.NewClientWithUserAgent(userAgent()), + baseUri: endpoint, + } +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/constants.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/constants.go new file mode 100644 index 000000000000..60915c4b5581 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/constants.go @@ -0,0 +1,379 @@ +package volumegroups + +import "strings" + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type ApplicationType string + +const ( + ApplicationTypeSAPNegativeHANA ApplicationType = "SAP-HANA" +) + +func PossibleValuesForApplicationType() []string { + return []string{ + string(ApplicationTypeSAPNegativeHANA), + } +} + +func parseApplicationType(input string) (*ApplicationType, error) { + vals := map[string]ApplicationType{ + "sap-hana": ApplicationTypeSAPNegativeHANA, + } + if v, ok := vals[strings.ToLower(input)]; ok { + return &v, nil + } + + // otherwise presume it's an undefined value and best-effort it + out := ApplicationType(input) + return &out, nil +} + +type AvsDataStore string + +const ( + AvsDataStoreDisabled AvsDataStore = "Disabled" + AvsDataStoreEnabled AvsDataStore = "Enabled" +) + +func PossibleValuesForAvsDataStore() []string { + return []string{ + string(AvsDataStoreDisabled), + string(AvsDataStoreEnabled), + } +} + +func parseAvsDataStore(input string) (*AvsDataStore, error) { + vals := map[string]AvsDataStore{ + "disabled": AvsDataStoreDisabled, + "enabled": AvsDataStoreEnabled, + } + if v, ok := vals[strings.ToLower(input)]; ok { + return &v, nil + } + + // otherwise presume it's an undefined value and best-effort it + out := AvsDataStore(input) + return &out, nil +} + +type ChownMode string + +const ( + ChownModeRestricted ChownMode = "Restricted" + ChownModeUnrestricted ChownMode = "Unrestricted" +) + +func PossibleValuesForChownMode() []string { + return []string{ + string(ChownModeRestricted), + string(ChownModeUnrestricted), + } +} + +func parseChownMode(input string) (*ChownMode, error) { + vals := map[string]ChownMode{ + "restricted": ChownModeRestricted, + "unrestricted": ChownModeUnrestricted, + } + if v, ok := vals[strings.ToLower(input)]; ok { + return &v, nil + } + + // otherwise presume it's an undefined value and best-effort it + out := ChownMode(input) + return &out, nil +} + +type EnableSubvolumes string + +const ( + EnableSubvolumesDisabled EnableSubvolumes = "Disabled" + EnableSubvolumesEnabled EnableSubvolumes = "Enabled" +) + +func PossibleValuesForEnableSubvolumes() []string { + return []string{ + string(EnableSubvolumesDisabled), + string(EnableSubvolumesEnabled), + } +} + +func parseEnableSubvolumes(input string) (*EnableSubvolumes, error) { + vals := map[string]EnableSubvolumes{ + "disabled": EnableSubvolumesDisabled, + "enabled": EnableSubvolumesEnabled, + } + if v, ok := vals[strings.ToLower(input)]; ok { + return &v, nil + } + + // otherwise presume it's an undefined value and best-effort it + out := EnableSubvolumes(input) + return &out, nil +} + +type EncryptionKeySource string + +const ( + EncryptionKeySourceMicrosoftPointKeyVault EncryptionKeySource = "Microsoft.KeyVault" + EncryptionKeySourceMicrosoftPointNetApp EncryptionKeySource = "Microsoft.NetApp" +) + +func PossibleValuesForEncryptionKeySource() []string { + return []string{ + string(EncryptionKeySourceMicrosoftPointKeyVault), + string(EncryptionKeySourceMicrosoftPointNetApp), + } +} + +func parseEncryptionKeySource(input string) (*EncryptionKeySource, error) { + vals := map[string]EncryptionKeySource{ + "microsoft.keyvault": EncryptionKeySourceMicrosoftPointKeyVault, + "microsoft.netapp": EncryptionKeySourceMicrosoftPointNetApp, + } + if v, ok := vals[strings.ToLower(input)]; ok { + return &v, nil + } + + // otherwise presume it's an undefined value and best-effort it + out := EncryptionKeySource(input) + return &out, nil +} + +type EndpointType string + +const ( + EndpointTypeDst EndpointType = "dst" + EndpointTypeSrc EndpointType = "src" +) + +func PossibleValuesForEndpointType() []string { + return []string{ + string(EndpointTypeDst), + string(EndpointTypeSrc), + } +} + +func parseEndpointType(input string) (*EndpointType, error) { + vals := map[string]EndpointType{ + "dst": EndpointTypeDst, + "src": EndpointTypeSrc, + } + if v, ok := vals[strings.ToLower(input)]; ok { + return &v, nil + } + + // otherwise presume it's an undefined value and best-effort it + out := EndpointType(input) + return &out, nil +} + +type NetworkFeatures string + +const ( + NetworkFeaturesBasic NetworkFeatures = "Basic" + NetworkFeaturesStandard NetworkFeatures = "Standard" +) + +func PossibleValuesForNetworkFeatures() []string { + return []string{ + string(NetworkFeaturesBasic), + string(NetworkFeaturesStandard), + } +} + +func parseNetworkFeatures(input string) (*NetworkFeatures, error) { + vals := map[string]NetworkFeatures{ + "basic": NetworkFeaturesBasic, + "standard": NetworkFeaturesStandard, + } + if v, ok := vals[strings.ToLower(input)]; ok { + return &v, nil + } + + // otherwise presume it's an undefined value and best-effort it + out := NetworkFeatures(input) + return &out, nil +} + +type ReplicationSchedule string + +const ( + ReplicationScheduleDaily ReplicationSchedule = "daily" + ReplicationScheduleHourly ReplicationSchedule = "hourly" + ReplicationScheduleOneZerominutely ReplicationSchedule = "_10minutely" +) + +func PossibleValuesForReplicationSchedule() []string { + return []string{ + string(ReplicationScheduleDaily), + string(ReplicationScheduleHourly), + string(ReplicationScheduleOneZerominutely), + } +} + +func parseReplicationSchedule(input string) (*ReplicationSchedule, error) { + vals := map[string]ReplicationSchedule{ + "daily": ReplicationScheduleDaily, + "hourly": ReplicationScheduleHourly, + "_10minutely": ReplicationScheduleOneZerominutely, + } + if v, ok := vals[strings.ToLower(input)]; ok { + return &v, nil + } + + // otherwise presume it's an undefined value and best-effort it + out := ReplicationSchedule(input) + return &out, nil +} + +type SecurityStyle string + +const ( + SecurityStyleNtfs SecurityStyle = "ntfs" + SecurityStyleUnix SecurityStyle = "unix" +) + +func PossibleValuesForSecurityStyle() []string { + return []string{ + string(SecurityStyleNtfs), + string(SecurityStyleUnix), + } +} + +func parseSecurityStyle(input string) (*SecurityStyle, error) { + vals := map[string]SecurityStyle{ + "ntfs": SecurityStyleNtfs, + "unix": SecurityStyleUnix, + } + if v, ok := vals[strings.ToLower(input)]; ok { + return &v, nil + } + + // otherwise presume it's an undefined value and best-effort it + out := SecurityStyle(input) + return &out, nil +} + +type ServiceLevel string + +const ( + ServiceLevelPremium ServiceLevel = "Premium" + ServiceLevelStandard ServiceLevel = "Standard" + ServiceLevelStandardZRS ServiceLevel = "StandardZRS" + ServiceLevelUltra ServiceLevel = "Ultra" +) + +func PossibleValuesForServiceLevel() []string { + return []string{ + string(ServiceLevelPremium), + string(ServiceLevelStandard), + string(ServiceLevelStandardZRS), + string(ServiceLevelUltra), + } +} + +func parseServiceLevel(input string) (*ServiceLevel, error) { + vals := map[string]ServiceLevel{ + "premium": ServiceLevelPremium, + "standard": ServiceLevelStandard, + "standardzrs": ServiceLevelStandardZRS, + "ultra": ServiceLevelUltra, + } + if v, ok := vals[strings.ToLower(input)]; ok { + return &v, nil + } + + // otherwise presume it's an undefined value and best-effort it + out := ServiceLevel(input) + return &out, nil +} + +type SmbAccessBasedEnumeration string + +const ( + SmbAccessBasedEnumerationDisabled SmbAccessBasedEnumeration = "Disabled" + SmbAccessBasedEnumerationEnabled SmbAccessBasedEnumeration = "Enabled" +) + +func PossibleValuesForSmbAccessBasedEnumeration() []string { + return []string{ + string(SmbAccessBasedEnumerationDisabled), + string(SmbAccessBasedEnumerationEnabled), + } +} + +func parseSmbAccessBasedEnumeration(input string) (*SmbAccessBasedEnumeration, error) { + vals := map[string]SmbAccessBasedEnumeration{ + "disabled": SmbAccessBasedEnumerationDisabled, + "enabled": SmbAccessBasedEnumerationEnabled, + } + if v, ok := vals[strings.ToLower(input)]; ok { + return &v, nil + } + + // otherwise presume it's an undefined value and best-effort it + out := SmbAccessBasedEnumeration(input) + return &out, nil +} + +type SmbNonBrowsable string + +const ( + SmbNonBrowsableDisabled SmbNonBrowsable = "Disabled" + SmbNonBrowsableEnabled SmbNonBrowsable = "Enabled" +) + +func PossibleValuesForSmbNonBrowsable() []string { + return []string{ + string(SmbNonBrowsableDisabled), + string(SmbNonBrowsableEnabled), + } +} + +func parseSmbNonBrowsable(input string) (*SmbNonBrowsable, error) { + vals := map[string]SmbNonBrowsable{ + "disabled": SmbNonBrowsableDisabled, + "enabled": SmbNonBrowsableEnabled, + } + if v, ok := vals[strings.ToLower(input)]; ok { + return &v, nil + } + + // otherwise presume it's an undefined value and best-effort it + out := SmbNonBrowsable(input) + return &out, nil +} + +type VolumeStorageToNetworkProximity string + +const ( + VolumeStorageToNetworkProximityDefault VolumeStorageToNetworkProximity = "Default" + VolumeStorageToNetworkProximityTOne VolumeStorageToNetworkProximity = "T1" + VolumeStorageToNetworkProximityTTwo VolumeStorageToNetworkProximity = "T2" +) + +func PossibleValuesForVolumeStorageToNetworkProximity() []string { + return []string{ + string(VolumeStorageToNetworkProximityDefault), + string(VolumeStorageToNetworkProximityTOne), + string(VolumeStorageToNetworkProximityTTwo), + } +} + +func parseVolumeStorageToNetworkProximity(input string) (*VolumeStorageToNetworkProximity, error) { + vals := map[string]VolumeStorageToNetworkProximity{ + "default": VolumeStorageToNetworkProximityDefault, + "t1": VolumeStorageToNetworkProximityTOne, + "t2": VolumeStorageToNetworkProximityTTwo, + } + if v, ok := vals[strings.ToLower(input)]; ok { + return &v, nil + } + + // otherwise presume it's an undefined value and best-effort it + out := VolumeStorageToNetworkProximity(input) + return &out, nil +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/id_netappaccount.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/id_netappaccount.go new file mode 100644 index 000000000000..f96e16183c88 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/id_netappaccount.go @@ -0,0 +1,127 @@ +package volumegroups + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +var _ resourceids.ResourceId = NetAppAccountId{} + +// NetAppAccountId is a struct representing the Resource ID for a Net App Account +type NetAppAccountId struct { + SubscriptionId string + ResourceGroupName string + NetAppAccountName string +} + +// NewNetAppAccountID returns a new NetAppAccountId struct +func NewNetAppAccountID(subscriptionId string, resourceGroupName string, netAppAccountName string) NetAppAccountId { + return NetAppAccountId{ + SubscriptionId: subscriptionId, + ResourceGroupName: resourceGroupName, + NetAppAccountName: netAppAccountName, + } +} + +// ParseNetAppAccountID parses 'input' into a NetAppAccountId +func ParseNetAppAccountID(input string) (*NetAppAccountId, error) { + parser := resourceids.NewParserFromResourceIdType(NetAppAccountId{}) + parsed, err := parser.Parse(input, false) + if err != nil { + return nil, fmt.Errorf("parsing %q: %+v", input, err) + } + + var ok bool + id := NetAppAccountId{} + + if id.SubscriptionId, ok = parsed.Parsed["subscriptionId"]; !ok { + return nil, fmt.Errorf("the segment 'subscriptionId' was not found in the resource id %q", input) + } + + if id.ResourceGroupName, ok = parsed.Parsed["resourceGroupName"]; !ok { + return nil, fmt.Errorf("the segment 'resourceGroupName' was not found in the resource id %q", input) + } + + if id.NetAppAccountName, ok = parsed.Parsed["netAppAccountName"]; !ok { + return nil, fmt.Errorf("the segment 'netAppAccountName' was not found in the resource id %q", input) + } + + return &id, nil +} + +// ParseNetAppAccountIDInsensitively parses 'input' case-insensitively into a NetAppAccountId +// note: this method should only be used for API response data and not user input +func ParseNetAppAccountIDInsensitively(input string) (*NetAppAccountId, error) { + parser := resourceids.NewParserFromResourceIdType(NetAppAccountId{}) + parsed, err := parser.Parse(input, true) + if err != nil { + return nil, fmt.Errorf("parsing %q: %+v", input, err) + } + + var ok bool + id := NetAppAccountId{} + + if id.SubscriptionId, ok = parsed.Parsed["subscriptionId"]; !ok { + return nil, fmt.Errorf("the segment 'subscriptionId' was not found in the resource id %q", input) + } + + if id.ResourceGroupName, ok = parsed.Parsed["resourceGroupName"]; !ok { + return nil, fmt.Errorf("the segment 'resourceGroupName' was not found in the resource id %q", input) + } + + if id.NetAppAccountName, ok = parsed.Parsed["netAppAccountName"]; !ok { + return nil, fmt.Errorf("the segment 'netAppAccountName' was not found in the resource id %q", input) + } + + return &id, nil +} + +// ValidateNetAppAccountID checks that 'input' can be parsed as a Net App Account ID +func ValidateNetAppAccountID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := ParseNetAppAccountID(v); err != nil { + errors = append(errors, err) + } + + return +} + +// ID returns the formatted Net App Account ID +func (id NetAppAccountId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.NetApp/netAppAccounts/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroupName, id.NetAppAccountName) +} + +// Segments returns a slice of Resource ID Segments which comprise this Net App Account ID +func (id NetAppAccountId) Segments() []resourceids.Segment { + return []resourceids.Segment{ + resourceids.StaticSegment("staticSubscriptions", "subscriptions", "subscriptions"), + resourceids.SubscriptionIdSegment("subscriptionId", "12345678-1234-9876-4563-123456789012"), + resourceids.StaticSegment("staticResourceGroups", "resourceGroups", "resourceGroups"), + resourceids.ResourceGroupSegment("resourceGroupName", "example-resource-group"), + resourceids.StaticSegment("staticProviders", "providers", "providers"), + resourceids.ResourceProviderSegment("staticMicrosoftNetApp", "Microsoft.NetApp", "Microsoft.NetApp"), + resourceids.StaticSegment("staticNetAppAccounts", "netAppAccounts", "netAppAccounts"), + resourceids.UserSpecifiedSegment("netAppAccountName", "netAppAccountValue"), + } +} + +// String returns a human-readable description of this Net App Account ID +func (id NetAppAccountId) String() string { + components := []string{ + fmt.Sprintf("Subscription: %q", id.SubscriptionId), + fmt.Sprintf("Resource Group Name: %q", id.ResourceGroupName), + fmt.Sprintf("Net App Account Name: %q", id.NetAppAccountName), + } + return fmt.Sprintf("Net App Account (%s)", strings.Join(components, "\n")) +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/id_volumegroup.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/id_volumegroup.go new file mode 100644 index 000000000000..d76205f10070 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/id_volumegroup.go @@ -0,0 +1,140 @@ +package volumegroups + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +var _ resourceids.ResourceId = VolumeGroupId{} + +// VolumeGroupId is a struct representing the Resource ID for a Volume Group +type VolumeGroupId struct { + SubscriptionId string + ResourceGroupName string + NetAppAccountName string + VolumeGroupName string +} + +// NewVolumeGroupID returns a new VolumeGroupId struct +func NewVolumeGroupID(subscriptionId string, resourceGroupName string, netAppAccountName string, volumeGroupName string) VolumeGroupId { + return VolumeGroupId{ + SubscriptionId: subscriptionId, + ResourceGroupName: resourceGroupName, + NetAppAccountName: netAppAccountName, + VolumeGroupName: volumeGroupName, + } +} + +// ParseVolumeGroupID parses 'input' into a VolumeGroupId +func ParseVolumeGroupID(input string) (*VolumeGroupId, error) { + parser := resourceids.NewParserFromResourceIdType(VolumeGroupId{}) + parsed, err := parser.Parse(input, false) + if err != nil { + return nil, fmt.Errorf("parsing %q: %+v", input, err) + } + + var ok bool + id := VolumeGroupId{} + + if id.SubscriptionId, ok = parsed.Parsed["subscriptionId"]; !ok { + return nil, fmt.Errorf("the segment 'subscriptionId' was not found in the resource id %q", input) + } + + if id.ResourceGroupName, ok = parsed.Parsed["resourceGroupName"]; !ok { + return nil, fmt.Errorf("the segment 'resourceGroupName' was not found in the resource id %q", input) + } + + if id.NetAppAccountName, ok = parsed.Parsed["netAppAccountName"]; !ok { + return nil, fmt.Errorf("the segment 'netAppAccountName' was not found in the resource id %q", input) + } + + if id.VolumeGroupName, ok = parsed.Parsed["volumeGroupName"]; !ok { + return nil, fmt.Errorf("the segment 'volumeGroupName' was not found in the resource id %q", input) + } + + return &id, nil +} + +// ParseVolumeGroupIDInsensitively parses 'input' case-insensitively into a VolumeGroupId +// note: this method should only be used for API response data and not user input +func ParseVolumeGroupIDInsensitively(input string) (*VolumeGroupId, error) { + parser := resourceids.NewParserFromResourceIdType(VolumeGroupId{}) + parsed, err := parser.Parse(input, true) + if err != nil { + return nil, fmt.Errorf("parsing %q: %+v", input, err) + } + + var ok bool + id := VolumeGroupId{} + + if id.SubscriptionId, ok = parsed.Parsed["subscriptionId"]; !ok { + return nil, fmt.Errorf("the segment 'subscriptionId' was not found in the resource id %q", input) + } + + if id.ResourceGroupName, ok = parsed.Parsed["resourceGroupName"]; !ok { + return nil, fmt.Errorf("the segment 'resourceGroupName' was not found in the resource id %q", input) + } + + if id.NetAppAccountName, ok = parsed.Parsed["netAppAccountName"]; !ok { + return nil, fmt.Errorf("the segment 'netAppAccountName' was not found in the resource id %q", input) + } + + if id.VolumeGroupName, ok = parsed.Parsed["volumeGroupName"]; !ok { + return nil, fmt.Errorf("the segment 'volumeGroupName' was not found in the resource id %q", input) + } + + return &id, nil +} + +// ValidateVolumeGroupID checks that 'input' can be parsed as a Volume Group ID +func ValidateVolumeGroupID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := ParseVolumeGroupID(v); err != nil { + errors = append(errors, err) + } + + return +} + +// ID returns the formatted Volume Group ID +func (id VolumeGroupId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.NetApp/netAppAccounts/%s/volumeGroups/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroupName, id.NetAppAccountName, id.VolumeGroupName) +} + +// Segments returns a slice of Resource ID Segments which comprise this Volume Group ID +func (id VolumeGroupId) Segments() []resourceids.Segment { + return []resourceids.Segment{ + resourceids.StaticSegment("staticSubscriptions", "subscriptions", "subscriptions"), + resourceids.SubscriptionIdSegment("subscriptionId", "12345678-1234-9876-4563-123456789012"), + resourceids.StaticSegment("staticResourceGroups", "resourceGroups", "resourceGroups"), + resourceids.ResourceGroupSegment("resourceGroupName", "example-resource-group"), + resourceids.StaticSegment("staticProviders", "providers", "providers"), + resourceids.ResourceProviderSegment("staticMicrosoftNetApp", "Microsoft.NetApp", "Microsoft.NetApp"), + resourceids.StaticSegment("staticNetAppAccounts", "netAppAccounts", "netAppAccounts"), + resourceids.UserSpecifiedSegment("netAppAccountName", "netAppAccountValue"), + resourceids.StaticSegment("staticVolumeGroups", "volumeGroups", "volumeGroups"), + resourceids.UserSpecifiedSegment("volumeGroupName", "volumeGroupValue"), + } +} + +// String returns a human-readable description of this Volume Group ID +func (id VolumeGroupId) String() string { + components := []string{ + fmt.Sprintf("Subscription: %q", id.SubscriptionId), + fmt.Sprintf("Resource Group Name: %q", id.ResourceGroupName), + fmt.Sprintf("Net App Account Name: %q", id.NetAppAccountName), + fmt.Sprintf("Volume Group Name: %q", id.VolumeGroupName), + } + return fmt.Sprintf("Volume Group (%s)", strings.Join(components, "\n")) +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/method_volumegroupscreate_autorest.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/method_volumegroupscreate_autorest.go new file mode 100644 index 000000000000..ee9616617b30 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/method_volumegroupscreate_autorest.go @@ -0,0 +1,79 @@ +package volumegroups + +import ( + "context" + "fmt" + "net/http" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/hashicorp/go-azure-helpers/polling" +) + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumeGroupsCreateOperationResponse struct { + Poller polling.LongRunningPoller + HttpResponse *http.Response +} + +// VolumeGroupsCreate ... +func (c VolumeGroupsClient) VolumeGroupsCreate(ctx context.Context, id VolumeGroupId, input VolumeGroupDetails) (result VolumeGroupsCreateOperationResponse, err error) { + req, err := c.preparerForVolumeGroupsCreate(ctx, id, input) + if err != nil { + err = autorest.NewErrorWithError(err, "volumegroups.VolumeGroupsClient", "VolumeGroupsCreate", nil, "Failure preparing request") + return + } + + result, err = c.senderForVolumeGroupsCreate(ctx, req) + if err != nil { + err = autorest.NewErrorWithError(err, "volumegroups.VolumeGroupsClient", "VolumeGroupsCreate", result.HttpResponse, "Failure sending request") + return + } + + return +} + +// VolumeGroupsCreateThenPoll performs VolumeGroupsCreate then polls until it's completed +func (c VolumeGroupsClient) VolumeGroupsCreateThenPoll(ctx context.Context, id VolumeGroupId, input VolumeGroupDetails) error { + result, err := c.VolumeGroupsCreate(ctx, id, input) + if err != nil { + return fmt.Errorf("performing VolumeGroupsCreate: %+v", err) + } + + if err := result.Poller.PollUntilDone(); err != nil { + return fmt.Errorf("polling after VolumeGroupsCreate: %+v", err) + } + + return nil +} + +// preparerForVolumeGroupsCreate prepares the VolumeGroupsCreate request. +func (c VolumeGroupsClient) preparerForVolumeGroupsCreate(ctx context.Context, id VolumeGroupId, input VolumeGroupDetails) (*http.Request, error) { + queryParameters := map[string]interface{}{ + "api-version": defaultApiVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsContentType("application/json; charset=utf-8"), + autorest.AsPut(), + autorest.WithBaseURL(c.baseUri), + autorest.WithPath(id.ID()), + autorest.WithJSON(input), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// senderForVolumeGroupsCreate sends the VolumeGroupsCreate request. The method will close the +// http.Response Body if it receives an error. +func (c VolumeGroupsClient) senderForVolumeGroupsCreate(ctx context.Context, req *http.Request) (future VolumeGroupsCreateOperationResponse, err error) { + var resp *http.Response + resp, err = c.Client.Send(req, azure.DoRetryWithRegistration(c.Client)) + if err != nil { + return + } + + future.Poller, err = polling.NewPollerFromResponse(ctx, resp, c.Client, req.Method) + return +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/method_volumegroupsdelete_autorest.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/method_volumegroupsdelete_autorest.go new file mode 100644 index 000000000000..b3cba81e1756 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/method_volumegroupsdelete_autorest.go @@ -0,0 +1,78 @@ +package volumegroups + +import ( + "context" + "fmt" + "net/http" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" + "github.com/hashicorp/go-azure-helpers/polling" +) + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumeGroupsDeleteOperationResponse struct { + Poller polling.LongRunningPoller + HttpResponse *http.Response +} + +// VolumeGroupsDelete ... +func (c VolumeGroupsClient) VolumeGroupsDelete(ctx context.Context, id VolumeGroupId) (result VolumeGroupsDeleteOperationResponse, err error) { + req, err := c.preparerForVolumeGroupsDelete(ctx, id) + if err != nil { + err = autorest.NewErrorWithError(err, "volumegroups.VolumeGroupsClient", "VolumeGroupsDelete", nil, "Failure preparing request") + return + } + + result, err = c.senderForVolumeGroupsDelete(ctx, req) + if err != nil { + err = autorest.NewErrorWithError(err, "volumegroups.VolumeGroupsClient", "VolumeGroupsDelete", result.HttpResponse, "Failure sending request") + return + } + + return +} + +// VolumeGroupsDeleteThenPoll performs VolumeGroupsDelete then polls until it's completed +func (c VolumeGroupsClient) VolumeGroupsDeleteThenPoll(ctx context.Context, id VolumeGroupId) error { + result, err := c.VolumeGroupsDelete(ctx, id) + if err != nil { + return fmt.Errorf("performing VolumeGroupsDelete: %+v", err) + } + + if err := result.Poller.PollUntilDone(); err != nil { + return fmt.Errorf("polling after VolumeGroupsDelete: %+v", err) + } + + return nil +} + +// preparerForVolumeGroupsDelete prepares the VolumeGroupsDelete request. +func (c VolumeGroupsClient) preparerForVolumeGroupsDelete(ctx context.Context, id VolumeGroupId) (*http.Request, error) { + queryParameters := map[string]interface{}{ + "api-version": defaultApiVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsContentType("application/json; charset=utf-8"), + autorest.AsDelete(), + autorest.WithBaseURL(c.baseUri), + autorest.WithPath(id.ID()), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// senderForVolumeGroupsDelete sends the VolumeGroupsDelete request. The method will close the +// http.Response Body if it receives an error. +func (c VolumeGroupsClient) senderForVolumeGroupsDelete(ctx context.Context, req *http.Request) (future VolumeGroupsDeleteOperationResponse, err error) { + var resp *http.Response + resp, err = c.Client.Send(req, azure.DoRetryWithRegistration(c.Client)) + if err != nil { + return + } + + future.Poller, err = polling.NewPollerFromResponse(ctx, resp, c.Client, req.Method) + return +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/method_volumegroupsget_autorest.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/method_volumegroupsget_autorest.go new file mode 100644 index 000000000000..c216597fce42 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/method_volumegroupsget_autorest.go @@ -0,0 +1,68 @@ +package volumegroups + +import ( + "context" + "net/http" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" +) + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumeGroupsGetOperationResponse struct { + HttpResponse *http.Response + Model *VolumeGroupDetails +} + +// VolumeGroupsGet ... +func (c VolumeGroupsClient) VolumeGroupsGet(ctx context.Context, id VolumeGroupId) (result VolumeGroupsGetOperationResponse, err error) { + req, err := c.preparerForVolumeGroupsGet(ctx, id) + if err != nil { + err = autorest.NewErrorWithError(err, "volumegroups.VolumeGroupsClient", "VolumeGroupsGet", nil, "Failure preparing request") + return + } + + result.HttpResponse, err = c.Client.Send(req, azure.DoRetryWithRegistration(c.Client)) + if err != nil { + err = autorest.NewErrorWithError(err, "volumegroups.VolumeGroupsClient", "VolumeGroupsGet", result.HttpResponse, "Failure sending request") + return + } + + result, err = c.responderForVolumeGroupsGet(result.HttpResponse) + if err != nil { + err = autorest.NewErrorWithError(err, "volumegroups.VolumeGroupsClient", "VolumeGroupsGet", result.HttpResponse, "Failure responding to request") + return + } + + return +} + +// preparerForVolumeGroupsGet prepares the VolumeGroupsGet request. +func (c VolumeGroupsClient) preparerForVolumeGroupsGet(ctx context.Context, id VolumeGroupId) (*http.Request, error) { + queryParameters := map[string]interface{}{ + "api-version": defaultApiVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsContentType("application/json; charset=utf-8"), + autorest.AsGet(), + autorest.WithBaseURL(c.baseUri), + autorest.WithPath(id.ID()), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// responderForVolumeGroupsGet handles the response to the VolumeGroupsGet request. The method always +// closes the http.Response Body. +func (c VolumeGroupsClient) responderForVolumeGroupsGet(resp *http.Response) (result VolumeGroupsGetOperationResponse, err error) { + err = autorest.Respond( + resp, + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result.Model), + autorest.ByClosing()) + result.HttpResponse = resp + + return +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/method_volumegroupslistbynetappaccount_autorest.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/method_volumegroupslistbynetappaccount_autorest.go new file mode 100644 index 000000000000..f37aba669c51 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/method_volumegroupslistbynetappaccount_autorest.go @@ -0,0 +1,69 @@ +package volumegroups + +import ( + "context" + "fmt" + "net/http" + + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" +) + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumeGroupsListByNetAppAccountOperationResponse struct { + HttpResponse *http.Response + Model *VolumeGroupList +} + +// VolumeGroupsListByNetAppAccount ... +func (c VolumeGroupsClient) VolumeGroupsListByNetAppAccount(ctx context.Context, id NetAppAccountId) (result VolumeGroupsListByNetAppAccountOperationResponse, err error) { + req, err := c.preparerForVolumeGroupsListByNetAppAccount(ctx, id) + if err != nil { + err = autorest.NewErrorWithError(err, "volumegroups.VolumeGroupsClient", "VolumeGroupsListByNetAppAccount", nil, "Failure preparing request") + return + } + + result.HttpResponse, err = c.Client.Send(req, azure.DoRetryWithRegistration(c.Client)) + if err != nil { + err = autorest.NewErrorWithError(err, "volumegroups.VolumeGroupsClient", "VolumeGroupsListByNetAppAccount", result.HttpResponse, "Failure sending request") + return + } + + result, err = c.responderForVolumeGroupsListByNetAppAccount(result.HttpResponse) + if err != nil { + err = autorest.NewErrorWithError(err, "volumegroups.VolumeGroupsClient", "VolumeGroupsListByNetAppAccount", result.HttpResponse, "Failure responding to request") + return + } + + return +} + +// preparerForVolumeGroupsListByNetAppAccount prepares the VolumeGroupsListByNetAppAccount request. +func (c VolumeGroupsClient) preparerForVolumeGroupsListByNetAppAccount(ctx context.Context, id NetAppAccountId) (*http.Request, error) { + queryParameters := map[string]interface{}{ + "api-version": defaultApiVersion, + } + + preparer := autorest.CreatePreparer( + autorest.AsContentType("application/json; charset=utf-8"), + autorest.AsGet(), + autorest.WithBaseURL(c.baseUri), + autorest.WithPath(fmt.Sprintf("%s/volumeGroups", id.ID())), + autorest.WithQueryParameters(queryParameters)) + return preparer.Prepare((&http.Request{}).WithContext(ctx)) +} + +// responderForVolumeGroupsListByNetAppAccount handles the response to the VolumeGroupsListByNetAppAccount request. The method always +// closes the http.Response Body. +func (c VolumeGroupsClient) responderForVolumeGroupsListByNetAppAccount(resp *http.Response) (result VolumeGroupsListByNetAppAccountOperationResponse, err error) { + err = autorest.Respond( + resp, + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&result.Model), + autorest.ByClosing()) + result.HttpResponse = resp + + return +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_exportpolicyrule.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_exportpolicyrule.go new file mode 100644 index 000000000000..e17395ba1a77 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_exportpolicyrule.go @@ -0,0 +1,22 @@ +package volumegroups + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type ExportPolicyRule struct { + AllowedClients *string `json:"allowedClients,omitempty"` + ChownMode *ChownMode `json:"chownMode,omitempty"` + Cifs *bool `json:"cifs,omitempty"` + HasRootAccess *bool `json:"hasRootAccess,omitempty"` + Kerberos5ReadOnly *bool `json:"kerberos5ReadOnly,omitempty"` + Kerberos5ReadWrite *bool `json:"kerberos5ReadWrite,omitempty"` + Kerberos5iReadOnly *bool `json:"kerberos5iReadOnly,omitempty"` + Kerberos5iReadWrite *bool `json:"kerberos5iReadWrite,omitempty"` + Kerberos5pReadOnly *bool `json:"kerberos5pReadOnly,omitempty"` + Kerberos5pReadWrite *bool `json:"kerberos5pReadWrite,omitempty"` + Nfsv3 *bool `json:"nfsv3,omitempty"` + Nfsv41 *bool `json:"nfsv41,omitempty"` + RuleIndex *int64 `json:"ruleIndex,omitempty"` + UnixReadOnly *bool `json:"unixReadOnly,omitempty"` + UnixReadWrite *bool `json:"unixReadWrite,omitempty"` +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_mounttargetproperties.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_mounttargetproperties.go new file mode 100644 index 000000000000..5844b80ce9dd --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_mounttargetproperties.go @@ -0,0 +1,11 @@ +package volumegroups + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type MountTargetProperties struct { + FileSystemId string `json:"fileSystemId"` + IPAddress *string `json:"ipAddress,omitempty"` + MountTargetId *string `json:"mountTargetId,omitempty"` + SmbServerFqdn *string `json:"smbServerFqdn,omitempty"` +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_placementkeyvaluepairs.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_placementkeyvaluepairs.go new file mode 100644 index 000000000000..1714b1662a1d --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_placementkeyvaluepairs.go @@ -0,0 +1,9 @@ +package volumegroups + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type PlacementKeyValuePairs struct { + Key string `json:"key"` + Value string `json:"value"` +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_replicationobject.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_replicationobject.go new file mode 100644 index 000000000000..6d145aa00798 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_replicationobject.go @@ -0,0 +1,12 @@ +package volumegroups + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type ReplicationObject struct { + EndpointType *EndpointType `json:"endpointType,omitempty"` + RemoteVolumeRegion *string `json:"remoteVolumeRegion,omitempty"` + RemoteVolumeResourceId string `json:"remoteVolumeResourceId"` + ReplicationId *string `json:"replicationId,omitempty"` + ReplicationSchedule *ReplicationSchedule `json:"replicationSchedule,omitempty"` +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumebackupproperties.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumebackupproperties.go new file mode 100644 index 000000000000..1141229b560a --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumebackupproperties.go @@ -0,0 +1,11 @@ +package volumegroups + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumeBackupProperties struct { + BackupEnabled *bool `json:"backupEnabled,omitempty"` + BackupPolicyId *string `json:"backupPolicyId,omitempty"` + PolicyEnforced *bool `json:"policyEnforced,omitempty"` + VaultId *string `json:"vaultId,omitempty"` +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegroup.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegroup.go new file mode 100644 index 000000000000..5e4df7bb9c04 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegroup.go @@ -0,0 +1,12 @@ +package volumegroups + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumeGroup struct { + Id *string `json:"id,omitempty"` + Location *string `json:"location,omitempty"` + Name *string `json:"name,omitempty"` + Properties *VolumeGroupListProperties `json:"properties,omitempty"` + Type *string `json:"type,omitempty"` +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegroupdetails.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegroupdetails.go new file mode 100644 index 000000000000..5f01af82c0f7 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegroupdetails.go @@ -0,0 +1,12 @@ +package volumegroups + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumeGroupDetails struct { + Id *string `json:"id,omitempty"` + Location *string `json:"location,omitempty"` + Name *string `json:"name,omitempty"` + Properties *VolumeGroupProperties `json:"properties,omitempty"` + Type *string `json:"type,omitempty"` +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegrouplist.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegrouplist.go new file mode 100644 index 000000000000..447c6e6e8588 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegrouplist.go @@ -0,0 +1,8 @@ +package volumegroups + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumeGroupList struct { + Value *[]VolumeGroup `json:"value,omitempty"` +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegrouplistproperties.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegrouplistproperties.go new file mode 100644 index 000000000000..5788ee516fc1 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegrouplistproperties.go @@ -0,0 +1,9 @@ +package volumegroups + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumeGroupListProperties struct { + GroupMetaData *VolumeGroupMetaData `json:"groupMetaData,omitempty"` + ProvisioningState *string `json:"provisioningState,omitempty"` +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegroupmetadata.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegroupmetadata.go new file mode 100644 index 000000000000..30441c6826d5 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegroupmetadata.go @@ -0,0 +1,13 @@ +package volumegroups + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumeGroupMetaData struct { + ApplicationIdentifier *string `json:"applicationIdentifier,omitempty"` + ApplicationType *ApplicationType `json:"applicationType,omitempty"` + DeploymentSpecId *string `json:"deploymentSpecId,omitempty"` + GlobalPlacementRules *[]PlacementKeyValuePairs `json:"globalPlacementRules,omitempty"` + GroupDescription *string `json:"groupDescription,omitempty"` + VolumesCount *int64 `json:"volumesCount,omitempty"` +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegroupproperties.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegroupproperties.go new file mode 100644 index 000000000000..0bab153b397e --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegroupproperties.go @@ -0,0 +1,10 @@ +package volumegroups + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumeGroupProperties struct { + GroupMetaData *VolumeGroupMetaData `json:"groupMetaData,omitempty"` + ProvisioningState *string `json:"provisioningState,omitempty"` + Volumes *[]VolumeGroupVolumeProperties `json:"volumes,omitempty"` +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegroupvolumeproperties.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegroupvolumeproperties.go new file mode 100644 index 000000000000..61bbdab9ce3e --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumegroupvolumeproperties.go @@ -0,0 +1,12 @@ +package volumegroups + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumeGroupVolumeProperties struct { + Id *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + Properties VolumeProperties `json:"properties"` + Tags *map[string]string `json:"tags,omitempty"` + Type *string `json:"type,omitempty"` +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumeproperties.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumeproperties.go new file mode 100644 index 000000000000..bf37d27af942 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumeproperties.go @@ -0,0 +1,54 @@ +package volumegroups + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumeProperties struct { + AvsDataStore *AvsDataStore `json:"avsDataStore,omitempty"` + BackupId *string `json:"backupId,omitempty"` + BaremetalTenantId *string `json:"baremetalTenantId,omitempty"` + CapacityPoolResourceId *string `json:"capacityPoolResourceId,omitempty"` + CloneProgress *int64 `json:"cloneProgress,omitempty"` + CoolAccess *bool `json:"coolAccess,omitempty"` + CoolnessPeriod *int64 `json:"coolnessPeriod,omitempty"` + CreationToken string `json:"creationToken"` + DataProtection *VolumePropertiesDataProtection `json:"dataProtection,omitempty"` + DefaultGroupQuotaInKiBs *int64 `json:"defaultGroupQuotaInKiBs,omitempty"` + DefaultUserQuotaInKiBs *int64 `json:"defaultUserQuotaInKiBs,omitempty"` + DeleteBaseSnapshot *bool `json:"deleteBaseSnapshot,omitempty"` + EnableSubvolumes *EnableSubvolumes `json:"enableSubvolumes,omitempty"` + Encrypted *bool `json:"encrypted,omitempty"` + EncryptionKeySource *EncryptionKeySource `json:"encryptionKeySource,omitempty"` + ExportPolicy *VolumePropertiesExportPolicy `json:"exportPolicy,omitempty"` + FileSystemId *string `json:"fileSystemId,omitempty"` + IsDefaultQuotaEnabled *bool `json:"isDefaultQuotaEnabled,omitempty"` + IsRestoring *bool `json:"isRestoring,omitempty"` + KerberosEnabled *bool `json:"kerberosEnabled,omitempty"` + KeyVaultPrivateEndpointResourceId *string `json:"keyVaultPrivateEndpointResourceId,omitempty"` + LdapEnabled *bool `json:"ldapEnabled,omitempty"` + MaximumNumberOfFiles *int64 `json:"maximumNumberOfFiles,omitempty"` + MountTargets *[]MountTargetProperties `json:"mountTargets,omitempty"` + NetworkFeatures *NetworkFeatures `json:"networkFeatures,omitempty"` + NetworkSiblingSetId *string `json:"networkSiblingSetId,omitempty"` + PlacementRules *[]PlacementKeyValuePairs `json:"placementRules,omitempty"` + ProtocolTypes *[]string `json:"protocolTypes,omitempty"` + ProvisioningState *string `json:"provisioningState,omitempty"` + ProximityPlacementGroup *string `json:"proximityPlacementGroup,omitempty"` + SecurityStyle *SecurityStyle `json:"securityStyle,omitempty"` + ServiceLevel *ServiceLevel `json:"serviceLevel,omitempty"` + SmbAccessBasedEnumeration *SmbAccessBasedEnumeration `json:"smbAccessBasedEnumeration,omitempty"` + SmbContinuouslyAvailable *bool `json:"smbContinuouslyAvailable,omitempty"` + SmbEncryption *bool `json:"smbEncryption,omitempty"` + SmbNonBrowsable *SmbNonBrowsable `json:"smbNonBrowsable,omitempty"` + SnapshotDirectoryVisible *bool `json:"snapshotDirectoryVisible,omitempty"` + SnapshotId *string `json:"snapshotId,omitempty"` + StorageToNetworkProximity *VolumeStorageToNetworkProximity `json:"storageToNetworkProximity,omitempty"` + SubnetId string `json:"subnetId"` + T2Network *string `json:"t2Network,omitempty"` + ThroughputMibps *float64 `json:"throughputMibps,omitempty"` + UnixPermissions *string `json:"unixPermissions,omitempty"` + UsageThreshold int64 `json:"usageThreshold"` + VolumeGroupName *string `json:"volumeGroupName,omitempty"` + VolumeSpecName *string `json:"volumeSpecName,omitempty"` + VolumeType *string `json:"volumeType,omitempty"` +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumepropertiesdataprotection.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumepropertiesdataprotection.go new file mode 100644 index 000000000000..5cde3ecbaacc --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumepropertiesdataprotection.go @@ -0,0 +1,10 @@ +package volumegroups + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumePropertiesDataProtection struct { + Backup *VolumeBackupProperties `json:"backup,omitempty"` + Replication *ReplicationObject `json:"replication,omitempty"` + Snapshot *VolumeSnapshotProperties `json:"snapshot,omitempty"` +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumepropertiesexportpolicy.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumepropertiesexportpolicy.go new file mode 100644 index 000000000000..227c8586d6f2 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumepropertiesexportpolicy.go @@ -0,0 +1,8 @@ +package volumegroups + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumePropertiesExportPolicy struct { + Rules *[]ExportPolicyRule `json:"rules,omitempty"` +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumesnapshotproperties.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumesnapshotproperties.go new file mode 100644 index 000000000000..a3f5011da3b0 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/model_volumesnapshotproperties.go @@ -0,0 +1,8 @@ +package volumegroups + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +type VolumeSnapshotProperties struct { + SnapshotPolicyId *string `json:"snapshotPolicyId,omitempty"` +} diff --git a/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/version.go b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/version.go new file mode 100644 index 000000000000..30dbbd9db030 --- /dev/null +++ b/vendor/github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups/version.go @@ -0,0 +1,12 @@ +package volumegroups + +import "fmt" + +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See NOTICE.txt in the project root for license information. + +const defaultApiVersion = "2022-05-01" + +func userAgent() string { + return fmt.Sprintf("hashicorp/go-azure-sdk/volumegroups/%s", defaultApiVersion) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 05832d8ed32d..2a7a4f4f10df 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -451,6 +451,7 @@ github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/capacitypoo github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/netappaccounts github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/snapshotpolicy github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/snapshots +github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumegroups github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumes github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2022-05-01/volumesreplication github.com/hashicorp/go-azure-sdk/resource-manager/nginx/2022-08-01 diff --git a/website/docs/d/netapp_volume_group_sap_hana.html.markdown b/website/docs/d/netapp_volume_group_sap_hana.html.markdown new file mode 100644 index 000000000000..4f29b9ea1c43 --- /dev/null +++ b/website/docs/d/netapp_volume_group_sap_hana.html.markdown @@ -0,0 +1,135 @@ +--- +subcategory: "NetApp" +layout: "azurerm" +page_title: "Azure Resource Manager: Data Source: azurerm_netapp_volume_group_sap_hana" +description: |- + Gets information about an existing Application Volume Group for SAP HANA application. +--- + +# Data Source: azurerm_netapp_volume_group_sap_hana + +Use this data source to access information about an existing Application Volume Group for SAP HANA application. + +## Example Usage + +```hcl +data "azurerm_netapp_volume_group_sap_hana" "example" { + name = "existing application volume group name" + resource_group_name = "resource group name where the account and volume group belong to" + account_name = "existing account where the application volume group belong to" +} + +output "id" { + value = data.azurerm_netapp_volume_group_sap_hana.example.id +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `account_name` - (Required) Name of the account where the application volume group belong to. + +* `name` - (Required) The name of this Application Volume Group for SAP HANA application. + +* `resource_group_name` - (Required) The name of the Resource Group where the Application Volume Group exists. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Application Volume Group. + +* `application_identifier` - The application identifier. + +* `group_description` - Volume group description. + +* `location` - The Azure Region where the Application Volume Group exists. + +* `volume` - A `volume` block as defined below. + +--- + +A `volume` block exports the following: + +* `capacity_pool_id` - The ID of the Capacity Pool. + +* `id` - Volume ID. + +* `name` - The name of this volume. + +* `proximity_placement_group_id` - The ID of the proximity placement group. + +* `security_style` - Volume security style. + +* `service_level` - The target performance of the file system. + +* `snapshot_directory_visible` - Is the .snapshot (NFS clients) path of a volume visible? + +* `storage_quota_in_gb` - The maximum Storage Quota allowed for a file system in Gigabytes. + +* `subnet_id` - The ID of the Subnet the NetApp Volume resides in. + +* `tags` - A mapping of tags assigned to the Application Volume Group. + +* `throughput_in_mibps` - Throughput of this volume in Mibps. + +* `volume_path` - A unique file path for the volume. + +* `volume_spec_name` - Volume spec name. + +* `data_protection_replication` - A `data_protection_replication` block as defined below. + +* `data_protection_snapshot_policy` - A `data_protection_snapshot_policy` block as defined below. + +* `export_policy_rule` - A `export_policy_rule` block as defined below. + +* `mount_ip_addresses` - A `mount_ip_addresses` block as defined below. + +* `protocols` - A `protocols` block as defined below. + +--- + +A `data_protection_replication` block exports the following: + +* `endpoint_type` - The endpoint type. + +* `remote_volume_location` - Location of the primary volume. + +* `remote_volume_resource_id` - Resource ID of the primary volume. + +* `replication_frequency` - Replication frequency. + +--- + +A `data_protection_snapshot_policy` block exports the following: + +* `snapshot_policy_id` - Resource ID of the snapshot policy to apply to the volume. + +--- + +A `export_policy_rule` block exports the following: + +* `allowed_clients` - A list of allowed clients IPv4 addresses. + +* `nfsv3_enabled` - Is the NFSv3 protocol enabled? + +* `nfsv41_enabled` - Is the NFSv4.1 enabled? + +* `root_access_enabled` - Is root access permitted to this volume? + +* `rule_index` - The index number of the rule. + +* `unix_read_only` - Is the file system on unix read only?. + +* `unix_read_write` - Is the file system on unix read and write?. + +--- + + + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +* `read` - (Defaults to 5 minutes) Used when retrieving the Application Volume Group. diff --git a/website/docs/r/netapp_volume_group_sap_hana.html.markdown b/website/docs/r/netapp_volume_group_sap_hana.html.markdown new file mode 100644 index 000000000000..6030c7340261 --- /dev/null +++ b/website/docs/r/netapp_volume_group_sap_hana.html.markdown @@ -0,0 +1,358 @@ +--- +subcategory: "NetApp" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_netapp_volume_group_sap_hana" +description: |- + Manages a Application Volume Group for SAP HANA application. +--- + +# azurerm_netapp_volume_group_sap_hana + +Manages a Application Volume Group for SAP HANA application. + +>Note: This feature is intended to be used for SAP-HANA workloads only, with several requirements, please refer to [Understand Azure NetApp Files application volume group for SAP HANA](https://learn.microsoft.com/en-us/azure/azure-netapp-files/application-volume-group-introduction) document as the starting point to understand this feature before using it with Terraform. + +## Example Usage + +```hcl +provider "azurerm" { + features { + resource_group { + prevent_deletion_if_contains_resources = false + } + } +} + +resource "random_string" "example" { + length = 12 + special = true +} + +locals { + admin_username = "exampleadmin" + admin_password = random_string.example.result +} + +resource "azurerm_resource_group" "example" { + name = "${var.prefix}-resources" + location = var.location +} + +resource "azurerm_virtual_network" "example" { + name = "${var.prefix}-vnet" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + address_space = ["10.6.0.0/16"] +} + +resource "azurerm_subnet" "example" { + name = "${var.prefix}-delegated-subnet" + resource_group_name = azurerm_resource_group.example.name + virtual_network_name = azurerm_virtual_network.example.name + address_prefixes = ["10.6.2.0/24"] + + delegation { + name = "testdelegation" + + service_delegation { + name = "Microsoft.Netapp/volumes" + actions = ["Microsoft.Network/networkinterfaces/*", "Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +resource "azurerm_subnet" "example1" { + name = "${var.prefix}-hosts-subnet" + resource_group_name = azurerm_resource_group.example.name + virtual_network_name = azurerm_virtual_network.example.name + address_prefixes = ["10.6.1.0/24"] +} + +resource "azurerm_proximity_placement_group" "example" { + name = "${var.prefix}-ppg" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name +} + +resource "azurerm_availability_set" "example" { + name = "${var.prefix}-avset" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + + proximity_placement_group_id = azurerm_proximity_placement_group.example.id +} + +resource "azurerm_network_interface" "example" { + name = "${var.prefix}-nic" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + + ip_configuration { + name = "internal" + subnet_id = azurerm_subnet.example1.id + private_ip_address_allocation = "Dynamic" + } +} + +resource "azurerm_linux_virtual_machine" "example" { + name = "${var.prefix}-vm" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + size = "Standard_M8ms" + admin_username = local.admin_username + admin_password = local.admin_password + disable_password_authentication = false + proximity_placement_group_id = azurerm_proximity_placement_group.example.id + availability_set_id = azurerm_availability_set.example.id + network_interface_ids = [ + azurerm_network_interface.example.id + ] + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "18.04-LTS" + version = "latest" + } + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } +} + +resource "azurerm_netapp_account" "example" { + name = "${var.prefix}-netapp-account" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + + depends_on = [ + azurerm_subnet.example, + azurerm_subnet.example1 + ] +} + +resource "azurerm_netapp_pool" "example" { + name = "${var.prefix}-netapp-pool" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + account_name = azurerm_netapp_account.example.name + service_level = "Standard" + size_in_tb = 8 + qos_type = "Manual" +} + +resource "azurerm_netapp_volume_group_sap_hana" "example" { + name = "${var.prefix}-netapp-volumegroup" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + account_name = azurerm_netapp_account.example.name + group_description = "Test volume group" + application_identifier = "TST" + + volume { + name = "${var.prefix}-netapp-volume-1" + volume_path = "my-unique-file-path-1" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.example.id + subnet_id = azurerm_subnet.example.id + proximity_placement_group_id = azurerm_proximity_placement_group.example.id + volume_spec_name = "data" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "foo" = "bar" + } + } + + volume { + name = "${var.prefix}-netapp-volume-2" + volume_path = "my-unique-file-path-2" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.example.id + subnet_id = azurerm_subnet.example.id + proximity_placement_group_id = azurerm_proximity_placement_group.example.id + volume_spec_name = "log" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + + tags = { + "foo" = "bar" + } + } + + volume { + name = "${var.prefix}-netapp-volume-3" + volume_path = "my-unique-file-path-3" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.example.id + subnet_id = azurerm_subnet.example.id + proximity_placement_group_id = azurerm_proximity_placement_group.example.id + volume_spec_name = "shared" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "Unix" + snapshot_directory_visible = false + + export_policy_rule { + rule_index = 1 + allowed_clients = "0.0.0.0/0" + nfsv3_enabled = false + nfsv41_enabled = true + unix_read_only = false + unix_read_write = true + root_access_enabled = false + } + } + + depends_on = [ + azurerm_linux_virtual_machine.example, + azurerm_proximity_placement_group.example + ] +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `account_name` - (Required) Name of the account where the application volume group belong to. Changing this forces a new Application Volume Group to be created and data will be lost. + +* `application_identifier` - (Required) The SAP System ID, maximum 3 characters, e.g. `SH9`. Changing this forces a new Application Volume Group to be created and data will be lost. + +* `group_description` - (Required) Volume group description. Changing this forces a new Application Volume Group to be created and data will be lost. + +* `location` - (Required) The Azure Region where the Application Volume Group should exist. Changing this forces a new Application Volume Group to be created and data will be lost. + +* `name` - (Required) The name which should be used for this Application Volume Group. Changing this forces a new Application Volume Group to be created and data will be lost. + +* `resource_group_name` - (Required) The name of the Resource Group where the Application Volume Group should exist. Changing this forces a new Application Volume Group to be created and data will be lost. + +* `volume` - (Required) One or more `volume` blocks as defined below. + +--- + +A `volume` block supports the following: + +* `capacity_pool_id` - (Required) The ID of the Capacity Pool. Changing this forces a new Application Volume Group to be created and data will be lost. + +* `name` - (Required) The name which should be used for this volume. Changing this forces a new Application Volume Group to be created and data will be lost. + +* `protocols` - (Required) The target volume protocol expressed as a list. Changing this forces a new Application Volume Group to be created and data will be lost. Supported values for Application Volume Group include `NFSv3` or `NFSv4.1`, multi-protocol is not supported and there are certain rules on which protocol is supporteed per volume spec, please check [Configure application volume groups for the SAP HANA REST API](https://learn.microsoft.com/en-us/azure/azure-netapp-files/configure-application-volume-group-sap-hana-api) document for details. + +* `proximity_placement_group_id` - (Required) The ID of the proximity placement group. Changing this forces a new Application Volume Group to be created and data will be lost. For SAP-HANA application, it is required to have PPG enabled so Azure NetApp Files can pin the volumes next to your compute resources, please check [Requirements and considerations for application volume group for SAP HANA](https://learn.microsoft.com/en-us/azure/azure-netapp-files/application-volume-group-considerations) for details and other requirements. + +* `security_style` - (Required) Volume security style. Possible value is `Unix`. Changing this forces a new Application Volume Group to be created and data will be lost. + +* `service_level` - (Required) Volume security style. Possible values are `Premium`, `Standard` and `Ultra`. Changing this forces a new Application Volume Group to be created and data will be lost. + +* `snapshot_directory_visible` - (Required) Specifies whether the .snapshot (NFS clients) path of a volume is visible. Changing this forces a new Application Volume Group to be created and data will be lost. + +* `storage_quota_in_gb` - (Required) The maximum Storage Quota allowed for a file system in Gigabytes. + +* `subnet_id` - (Required) The ID of the Subnet the NetApp Volume resides in, which must have the `Microsoft.NetApp/volumes` delegation. Changing this forces a new Application Volume Group to be created and data will be lost. + +* `throughput_in_mibps` - (Required) Throughput of this volume in Mibps. + +* `volume_path` - (Required) A unique file path for the volume. Changing this forces a new Application Volume Group to be created and data will be lost. + +* `volume_spec_name` - (Required) Volume specification name. Possible values are `data`, `log`, `shared`, `data-backup` and `log-backup`. Changing this forces a new Application Volume Group to be created and data will be lost. + +* `tags` - (Optional) A mapping of tags which should be assigned to the Application Volume Group. + +* `export_policy_rule` - (Required) One or more `export_policy_rule` blocks as defined below. + +* `data_protection_replication` - (Optional) A `data_protection_replication` block as defined below. Changing this forces a new Application Volume Group to be created and data will be lost. + +* `data_protection_snapshot_policy` - (Optional) A `data_protection_snapshot_policy` block as defined below. + +--- + +A `data_protection_replication` block is used when enabling the Cross-Region Replication (CRR) data protection option by deploying two Azure NetApp Files Volumes, one to be a primary volume and the other one will be the secondary, the secondary will have this block and will reference the primary volume, not all volume spec types are supported, please refer to [Configure application volume groups for the SAP HANA REST API](https://learn.microsoft.com/en-us/azure/azure-netapp-files/configure-application-volume-group-sap-hana-api) for detauls. Each volume must be in a supported [region pair](https://docs.microsoft.com/azure/azure-netapp-files/cross-region-replication-introduction#supported-region-pairs). + +This block supports the following: + +* `remote_volume_location` - (Required) Location of the primary volume. Changing this forces a new Application Volume Group to be created and data will be lost. + +* `remote_volume_resource_id` - (Required) Resource ID of the primary volume. + +* `replication_frequency` - (Required) eplication frequency. Possible values are `10minutes`, `daily` and `hourly`. + +* `endpoint_type` - (Optional) The endpoint type. Possible values are `dst` and `src`. Defaults to `dst`. + +--- + +A `data_protection_snapshot_policy` block supports the following: + +* `snapshot_policy_id` - (Required) Resource ID of the snapshot policy to apply to the volume. + +--- + +A `export_policy_rule` block supports the following: + +* `allowed_clients` - (Required) A comma-sperated list of allowed client IPv4 addresses. + +* `nfsv3_enabled` - (Required) Enables NFSv3. Please note that this cannot be enabled if volume has NFSv4.1 as its protocol. + +* `nfsv41_enabled` - (Required) Enables NFSv4.1. Please note that this cannot be enabled if volume has NFSv3 as its protocol. + +* `root_access_enabled` - (Optional) Is root access permitted to this volume? Defaults to `true`. + +* `rule_index` - (Required) The index number of the rule, must start at 1 and maximum 5. + +* `unix_read_only` - (Optional) Is the file system on unix read only? Defaults to `false. + +* `unix_read_write` - (Optional) Is the file system on unix read and write? Defaults to `true`. + +--- + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Application Volume Group. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +* `create` - (Defaults to 1 hour and 30 minutes) Used when creating the Application Volume Group. +* `read` - (Defaults to 5 minutes) Used when retrieving the Application Volume Group. +* `update` - (Defaults to 2 hours) Used when updating the Application Volume Group. +* `delete` - (Defaults to 2 hours) Used when deleting the Application Volume Group. + +## Import + +Application Volume Groups can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_netapp_volume_group_sap_hana.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mytest-rg/providers/Microsoft.NetApp/netAppAccounts/netapp-account-test/volumeGroups/netapp-volumegroup-test +```