From 82ecd52a4ba50f99a2b7edd08266a8305d98f55d Mon Sep 17 00:00:00 2001 From: Paulo Marques Date: Mon, 10 Feb 2025 14:44:08 -0700 Subject: [PATCH] New Resource: `azurerm_netapp_volume_group_oracle` (#28391) --- .../avg_oracle_availability_zone/README.md | 9 + .../avg_oracle_availability_zone/main.tf | 119 ++ .../avg_oracle_availability_zone/variables.tf | 7 + .../avg_oracle_cmkSystemAssigned/README.md | 11 + .../avg_oracle_cmkSystemAssigned/main.tf | 240 +++ .../avg_oracle_cmkSystemAssigned/variables.tf | 11 + .../README.md | 9 + .../main.tf | 254 +++ .../variables.tf | 7 + .../avg_oracle_snapshot_policy/README.md | 9 + .../netapp/avg_oracle_snapshot_policy/main.tf | 138 ++ .../avg_oracle_snapshot_policy/variables.tf | 7 + internal/services/netapp/README.md | 90 +- internal/services/netapp/models/models.go | 82 +- ...netapp_account_encryption_resource_test.go | 350 ++-- .../netapp/netapp_snapshot_policy_resource.go | 106 +- .../netapp_volume_group_oracle_data_source.go | 298 ++++ ...pp_volume_group_oracle_data_source_test.go | 42 + .../netapp_volume_group_oracle_resource.go | 550 +++++++ ...etapp_volume_group_oracle_resource_test.go | 1209 ++++++++++++++ ...etapp_volume_group_sap_hana_data_source.go | 33 +- ..._volume_group_sap_hana_data_source_test.go | 10 +- .../netapp_volume_group_sap_hana_resource.go | 55 +- ...app_volume_group_sap_hana_resource_test.go | 584 ++----- .../services/netapp/netapp_volume_helper.go | 211 ++- .../netapp_volume_quota_rule_resource.go | 79 +- .../netapp_volume_quota_rule_resource_test.go | 4 +- .../services/netapp/netapp_volume_resource.go | 4 +- .../netapp/netapp_volume_resource_test.go | 189 +-- internal/services/netapp/registration.go | 6 +- .../services/netapp/validate/account_id.go | 1 + .../netapp/validate/validate_helper.go | 33 + ...e_volumes_export_policy_validation_test.go | 104 ++ .../volume_group_oracle_volumes_validation.go | 202 +++ ...me_group_oracle_volumes_validation_test.go | 1453 +++++++++++++++++ ...a_volumes_export_policy_validation_test.go | 4 +- ...olume_group_sap_hana_volumes_validation.go | 125 +- ..._group_sap_hana_volumes_validation_test.go | 179 +- ...group_volumes_export_policy_validation.go} | 2 +- .../netapp_volume_group_oracle.html.markdown | 139 ++ .../netapp_volume_group_oracle.html.markdown | 247 +++ ...netapp_volume_group_sap_hana.html.markdown | 6 +- ...are_netapp_volume_attachment.html.markdown | 6 +- 43 files changed, 6216 insertions(+), 1008 deletions(-) create mode 100644 examples/netapp/avg_oracle_availability_zone/README.md create mode 100644 examples/netapp/avg_oracle_availability_zone/main.tf create mode 100644 examples/netapp/avg_oracle_availability_zone/variables.tf create mode 100644 examples/netapp/avg_oracle_cmkSystemAssigned/README.md create mode 100644 examples/netapp/avg_oracle_cmkSystemAssigned/main.tf create mode 100644 examples/netapp/avg_oracle_cmkSystemAssigned/variables.tf create mode 100644 examples/netapp/avg_oracle_proximity_placement_group/README.md create mode 100644 examples/netapp/avg_oracle_proximity_placement_group/main.tf create mode 100644 examples/netapp/avg_oracle_proximity_placement_group/variables.tf create mode 100644 examples/netapp/avg_oracle_snapshot_policy/README.md create mode 100644 examples/netapp/avg_oracle_snapshot_policy/main.tf create mode 100644 examples/netapp/avg_oracle_snapshot_policy/variables.tf create mode 100644 internal/services/netapp/netapp_volume_group_oracle_data_source.go create mode 100644 internal/services/netapp/netapp_volume_group_oracle_data_source_test.go create mode 100644 internal/services/netapp/netapp_volume_group_oracle_resource.go create mode 100644 internal/services/netapp/netapp_volume_group_oracle_resource_test.go create mode 100644 internal/services/netapp/validate/validate_helper.go create mode 100644 internal/services/netapp/validate/volume_group_oracle_volumes_export_policy_validation_test.go create mode 100644 internal/services/netapp/validate/volume_group_oracle_volumes_validation.go create mode 100644 internal/services/netapp/validate/volume_group_oracle_volumes_validation_test.go rename internal/services/netapp/validate/{volume_group_sap_hana_volumes_export_policy_validation.go => volume_group_volumes_export_policy_validation.go} (91%) create mode 100644 website/docs/d/netapp_volume_group_oracle.html.markdown create mode 100644 website/docs/r/netapp_volume_group_oracle.html.markdown diff --git a/examples/netapp/avg_oracle_availability_zone/README.md b/examples/netapp/avg_oracle_availability_zone/README.md new file mode 100644 index 0000000000000..ce5f683a558be --- /dev/null +++ b/examples/netapp/avg_oracle_availability_zone/README.md @@ -0,0 +1,9 @@ +## Example: NetApp Files Application Volume Group For Oracle with Availability Zone + +This example provisions an application volume group for Oracle resource using availability zone. + +### Variables + +* `prefix` - (Required) The prefix used for all resources in this example. + +* `location` - (Required) The Azure Region in which the resources in this example should be created. diff --git a/examples/netapp/avg_oracle_availability_zone/main.tf b/examples/netapp/avg_oracle_availability_zone/main.tf new file mode 100644 index 0000000000000..7e53af375c9db --- /dev/null +++ b/examples/netapp/avg_oracle_availability_zone/main.tf @@ -0,0 +1,119 @@ +provider "azurerm" { + features { + netapp { + prevent_volume_destruction = true + } + } +} + +resource "azurerm_resource_group" "example" { + name = "${var.prefix}-resources" + location = var.location + + tags = { + "SkipNRMSNSG" = "true" + } +} + +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.47.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.47.2.0/24"] + + delegation { + name = "exampledelegation" + + service_delegation { + name = "Microsoft.Netapp/volumes" + actions = ["Microsoft.Network/networkinterfaces/*", "Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +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 + ] +} + +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 = 4 + qos_type = "Manual" +} + + +resource "azurerm_netapp_volume_group_oracle" "example" { + name = "${var.prefix}-NetAppVolumeGroupOracle" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + account_name = azurerm_netapp_account.example.name + group_description = "Example volume group for Oracle" + application_identifier = "TST" + + volume { + name = "${var.prefix}-volume-ora1" + volume_path = "${var.prefix}-my-unique-file-ora-path-1" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.example.id + subnet_id = azurerm_subnet.example.id + zone = "1" + volume_spec_name = "ora-data1" + 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 + } + } + + volume { + name = "${var.prefix}-volume-oraLog" + volume_path = "${var.prefix}-my-unique-file-oralog-path" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.example.id + subnet_id = azurerm_subnet.example.id + zone = "1" + volume_spec_name = "ora-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 + } + } +} diff --git a/examples/netapp/avg_oracle_availability_zone/variables.tf b/examples/netapp/avg_oracle_availability_zone/variables.tf new file mode 100644 index 0000000000000..31dcff3023536 --- /dev/null +++ b/examples/netapp/avg_oracle_availability_zone/variables.tf @@ -0,0 +1,7 @@ +variable "location" { + description = "The Azure location where all resources in this example should be created." +} + +variable "prefix" { + description = "The prefix used for all resources used by this NetApp Volume" +} diff --git a/examples/netapp/avg_oracle_cmkSystemAssigned/README.md b/examples/netapp/avg_oracle_cmkSystemAssigned/README.md new file mode 100644 index 0000000000000..f30f74ee78988 --- /dev/null +++ b/examples/netapp/avg_oracle_cmkSystemAssigned/README.md @@ -0,0 +1,11 @@ +## Example: NetApp Files Application Volume Group For Oracle with System Assigned Identity and Customer Managed Key + +This example provisions an application volume group for Oracle resource using System Assigned Identity and Customer Managed Key. + +### Variables + +* `prefix` - (Required) The prefix used for all resources in this example. + +* `location` - (Required) The Azure Region in which the resources in this example should be created. + +* `tenant_id` - (Required) The Tenant ID for key vault access policy. diff --git a/examples/netapp/avg_oracle_cmkSystemAssigned/main.tf b/examples/netapp/avg_oracle_cmkSystemAssigned/main.tf new file mode 100644 index 0000000000000..1ba6f5af2d717 --- /dev/null +++ b/examples/netapp/avg_oracle_cmkSystemAssigned/main.tf @@ -0,0 +1,240 @@ +provider "azurerm" { + features { + netapp { + prevent_volume_destruction = true + } + } +} + +data "azurerm_client_config" "current" { +} + +resource "azurerm_resource_group" "example" { + name = "${var.prefix}-resources" + location = var.location + + tags = { + "SkipNRMSNSG" = "true" + } +} + +resource "azurerm_netapp_account" "example" { + name = "${var.prefix}-account" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + + identity { + type = "SystemAssigned" + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z" + } +} + +resource "azurerm_key_vault" "example" { + name = "${var.prefix}anfakv" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + enabled_for_disk_encryption = true + enabled_for_deployment = true + enabled_for_template_deployment = true + purge_protection_enabled = true + tenant_id = var.tenant_id + sku_name = "standard" + + access_policy { + tenant_id = azurerm_netapp_account.example.identity.0.tenant_id + object_id = data.azurerm_client_config.current.object_id + + certificate_permissions = [] + secret_permissions = [] + storage_permissions = [] + key_permissions = [ + "Get", + "Create", + "Delete", + "WrapKey", + "UnwrapKey", + "GetRotationPolicy", + "SetRotationPolicy", + ] + } + + access_policy { + tenant_id = azurerm_netapp_account.example.identity.0.tenant_id + object_id = azurerm_netapp_account.example.identity.0.principal_id + + certificate_permissions = [] + secret_permissions = [] + storage_permissions = [] + key_permissions = [ + "Get", + "Encrypt", + "Decrypt" + ] + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z" + } +} + +resource "azurerm_key_vault_key" "example" { + name = "${var.prefix}anfenckey" + key_vault_id = azurerm_key_vault.example.id + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} + +resource "azurerm_netapp_account_encryption" "example" { + netapp_account_id = azurerm_netapp_account.example.id + system_assigned_identity_principal_id = azurerm_netapp_account.example.identity.0.principal_id + encryption_key = azurerm_key_vault_key.example.versionless_id +} + +resource "azurerm_virtual_network" "example" { + name = "${var.prefix}-virtual-network" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + address_space = ["10.6.0.0/16"] + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_subnet" "example-delegated" { + 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.1.0/24"] + + delegation { + name = "exampledelegation" + + service_delegation { + name = "Microsoft.Netapp/volumes" + actions = ["Microsoft.Network/networkinterfaces/*", "Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +resource "azurerm_subnet" "example-non-delegated" { + name = "${var.prefix}-non-delegated-subnet" + resource_group_name = azurerm_resource_group.example.name + virtual_network_name = azurerm_virtual_network.example.name + address_prefixes = ["10.6.0.0/24"] +} + +resource "azurerm_private_endpoint" "example" { + name = "${var.prefix}-pe-akv" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + subnet_id = azurerm_subnet.example-non-delegated.id + + private_service_connection { + name = "${var.prefix}-pe-sc-akv" + private_connection_resource_id = azurerm_key_vault.example.id + is_manual_connection = false + subresource_names = ["Vault"] + } + + tags = { + CreatedOnDate = "2023-10-03T19:58:43.6509795Z" + } +} + +resource "azurerm_netapp_pool" "example" { + name = "${var.prefix}-capacity-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 = 4 + qos_type = "Manual" + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + + depends_on = [ + azurerm_netapp_account_encryption.example + ] +} + +resource "azurerm_netapp_volume_group_oracle" "example" { + name = "${var.prefix}-volume-group-oracle" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + account_name = azurerm_netapp_account.example.name + group_description = "Example volume group for Oracle" + application_identifier = "TST" + + volume { + name = "${var.prefix}-volume-ora1" + volume_path = "${var.prefix}-my-unique-file-ora-path-1" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.example.id + subnet_id = azurerm_subnet.example-delegated.id + zone = "1" + volume_spec_name = "ora-data1" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "unix" + snapshot_directory_visible = false + encryption_key_source = "Microsoft.KeyVault" + key_vault_private_endpoint_id = azurerm_private_endpoint.example.id + network_features = "Standard" + + 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 + } + } + + volume { + name = "${var.prefix}-volume-oraLog" + volume_path = "${var.prefix}-my-unique-file-oralog-path" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.example.id + subnet_id = azurerm_subnet.example-delegated.id + zone = "1" + volume_spec_name = "ora-log" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "unix" + snapshot_directory_visible = false + encryption_key_source = "Microsoft.KeyVault" + key_vault_private_endpoint_id = azurerm_private_endpoint.example.id + network_features = "Standard" + + 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 + } + } +} diff --git a/examples/netapp/avg_oracle_cmkSystemAssigned/variables.tf b/examples/netapp/avg_oracle_cmkSystemAssigned/variables.tf new file mode 100644 index 0000000000000..f96daf16eedae --- /dev/null +++ b/examples/netapp/avg_oracle_cmkSystemAssigned/variables.tf @@ -0,0 +1,11 @@ +variable "location" { + description = "The Azure location where all resources in this example should be created." +} + +variable "prefix" { + description = "The prefix used for all resources used by this NetApp Volume" +} + +variable "tenant_id" { + description = "The Azure tenant ID used to create the user-assigned identity" +} diff --git a/examples/netapp/avg_oracle_proximity_placement_group/README.md b/examples/netapp/avg_oracle_proximity_placement_group/README.md new file mode 100644 index 0000000000000..aaa8ce42bc9ee --- /dev/null +++ b/examples/netapp/avg_oracle_proximity_placement_group/README.md @@ -0,0 +1,9 @@ +## Example: NetApp Files Application Volume Group For Oracle with Proximity Placement Group + +This example provisions an application volume group for Oracle resource using proximity placement group. + +### Variables + +* `prefix` - (Required) The prefix used for all resources in this example. + +* `location` - (Required) The Azure Region in which the resources in this example should be created. diff --git a/examples/netapp/avg_oracle_proximity_placement_group/main.tf b/examples/netapp/avg_oracle_proximity_placement_group/main.tf new file mode 100644 index 0000000000000..f3e6797674180 --- /dev/null +++ b/examples/netapp/avg_oracle_proximity_placement_group/main.tf @@ -0,0 +1,254 @@ +provider "azurerm" { + features { + netapp { + prevent_volume_destruction = true + } + } +} + +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 + + tags = { + "SkipNRMSNSG" = "true" + } +} + +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 = "exampledelegation" + + service_delegation { + name = "Microsoft.Netapp/volumes" + actions = ["Microsoft.Network/networkinterfaces/*", "Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +resource "azurerm_user_assigned_identity" "example" { + name = "${var.prefix}-user-assigned-identity" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z" + } +} + +resource "azurerm_network_security_group" "example" { + name = "${var.prefix}-nsg" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +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_subnet_network_security_group_association" "example" { + subnet_id = azurerm_subnet.example.id + network_security_group_id = azurerm_network_security_group.example.id +} + +resource "azurerm_proximity_placement_group" "example" { + name = "${var.prefix}-proximity-placement-group" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_availability_set" "example" { + name = "${var.prefix}-availability-set" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + + platform_update_domain_count = 2 + platform_fault_domain_count = 2 + + proximity_placement_group_id = azurerm_proximity_placement_group.example.id + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +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" + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +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_D2s_v4" + 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 = "0001-com-ubuntu-server-jammy" + sku = "22_04-lts" + version = "laexample" + } + + patch_assessment_mode = "AutomaticByPlatform" + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } + + identity { + type = "SystemAssigned, UserAssigned" + identity_ids = [ + azurerm_user_assigned_identity.example.id + ] + } + + tags = { + "AzSecPackAutoConfigReady" = "true", + "platformsettings.host_environment.service.platform_optedin_for_rootcerts" = "true", + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true", + "Owner" = "exampleuser" + } +} + +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 + ] +} + +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 = 4 + qos_type = "Manual" +} + +resource "azurerm_netapp_volume_group_oracle" "example" { + name = "${var.prefix}-NetAppVolumeGroupOracle" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + account_name = azurerm_netapp_account.example.name + group_description = "Example volume group for Oracle" + application_identifier = "TST" + + volume { + name = "${var.prefix}-NetAppVolume-Ora1" + volume_path = "${var.prefix}-my-unique-file-ora-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 = "ora-data1" + 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 + } + } + + volume { + name = "${var.prefix}-NetAppVolume-OraLog" + volume_path = "${var.prefix}-my-unique-file-oralog-path" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.example.id + subnet_id = azurerm_subnet.example.id + zone = "1" + volume_spec_name = "ora-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 + } + } + + depends_on = [ + azurerm_linux_virtual_machine.example, + azurerm_proximity_placement_group.example + ] +} diff --git a/examples/netapp/avg_oracle_proximity_placement_group/variables.tf b/examples/netapp/avg_oracle_proximity_placement_group/variables.tf new file mode 100644 index 0000000000000..31dcff3023536 --- /dev/null +++ b/examples/netapp/avg_oracle_proximity_placement_group/variables.tf @@ -0,0 +1,7 @@ +variable "location" { + description = "The Azure location where all resources in this example should be created." +} + +variable "prefix" { + description = "The prefix used for all resources used by this NetApp Volume" +} diff --git a/examples/netapp/avg_oracle_snapshot_policy/README.md b/examples/netapp/avg_oracle_snapshot_policy/README.md new file mode 100644 index 0000000000000..900401bdca870 --- /dev/null +++ b/examples/netapp/avg_oracle_snapshot_policy/README.md @@ -0,0 +1,9 @@ +## Example: NetApp Files Application Volume Group For Oracle with Snapshot Policy + +This example provisions an application volume group for Oracle resource with snapshot policy. + +### Variables + +* `prefix` - (Required) The prefix used for all resources in this example. + +* `location` - (Required) The Azure Region in which the resources in this example should be created. diff --git a/examples/netapp/avg_oracle_snapshot_policy/main.tf b/examples/netapp/avg_oracle_snapshot_policy/main.tf new file mode 100644 index 0000000000000..625dd47d6cfee --- /dev/null +++ b/examples/netapp/avg_oracle_snapshot_policy/main.tf @@ -0,0 +1,138 @@ +provider "azurerm" { + features { + netapp { + prevent_volume_destruction = true + } + } +} + +resource "azurerm_resource_group" "example" { + name = "${var.prefix}-resources" + location = var.location + + tags = { + "SkipNRMSNSG" = "true" + } +} + +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 = "exampledelegation" + + service_delegation { + name = "Microsoft.Netapp/volumes" + actions = ["Microsoft.Network/networkinterfaces/*", "Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +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 + ] +} + +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 = 4 + qos_type = "Manual" +} + +resource "azurerm_netapp_snapshot_policy" "example" { + name = "${var.prefix}-snapshot-policy" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + account_name = azurerm_netapp_account.example.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_oracle" "example" { + name = "${var.prefix}-volume-group-oracle" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + account_name = azurerm_netapp_account.example.name + group_description = "Example volume group for Oracle" + application_identifier = "TST" + + volume { + name = "${var.prefix}-volume-ora1" + volume_path = "${var.prefix}-my-unique-file-ora-path-1" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.example.id + subnet_id = azurerm_subnet.example.id + zone = "1" + volume_spec_name = "ora-data1" + 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 + } + } + + volume { + name = "${var.prefix}-volume-oraLog" + volume_path = "${var.prefix}-my-unique-file-oralog-path" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.example.id + subnet_id = azurerm_subnet.example.id + zone = "1" + volume_spec_name = "ora-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 + } + } +} diff --git a/examples/netapp/avg_oracle_snapshot_policy/variables.tf b/examples/netapp/avg_oracle_snapshot_policy/variables.tf new file mode 100644 index 0000000000000..31dcff3023536 --- /dev/null +++ b/examples/netapp/avg_oracle_snapshot_policy/variables.tf @@ -0,0 +1,7 @@ +variable "location" { + description = "The Azure location where all resources in this example should be created." +} + +variable "prefix" { + description = "The prefix used for all resources used by this NetApp Volume" +} diff --git a/internal/services/netapp/README.md b/internal/services/netapp/README.md index afdb8f6741bb6..85a2cce458391 100644 --- a/internal/services/netapp/README.md +++ b/internal/services/netapp/README.md @@ -9,10 +9,12 @@ This document gives insights into who is maintaining this service and includes d ## Acceptance Tests -- There is lack of SMB-related acceptance tests because it requires Active Directory Domain Controller infrastructure which is not easily automatable. SMB-related tests can only be tested if the infrastructure is setup beforehand which is not that trivial. We should not require SMB tests unless it comes with Domain Controller setup automation. Without automation, the SMB acceptance tests will fail and cause disruptions in CI/bulk testing. +- There is lack of SMB-related acceptance tests because it requires Active Directory Domain Controller infrastructure which is not easy to automate properly. SMB-related tests can only be tested if the infrastructure is setup beforehand which is not that trivial. We should not require SMB tests unless it comes with Domain Controller setup automation. Without automation, the SMB acceptance tests will fail and cause disruptions in CI/bulk testing. - New tests failing should not be accepted. +- For Azure NetApp Files, some features are highly dependent on specific regions, that's why for some acceptance tests, we will see regions defined there instead of for example `data.Locations.Primary` within the templates, this is expected and should not be changed. + ## Polling functions - Some Netapp resources requires an extra type of polling mechanism. For example: @@ -26,6 +28,42 @@ if err := waitForVolumeCreateOrUpdate(ctx, client, id); err != nil { This is because some operations return from regular SDK polling as completed but due to several factors it is still in progress (e.g. ARM caching, software and hardware layer sync delays, etc.). These wait functions are necessary and should not be removed. +- Do not approve Pull Requests that relies on `ThenPoll()` methods, e.g. `DeleteThenPoll()`, we should not use those for volume related operations due to some unknown [issues](https://github.com/hashicorp/pandora/issues/4571) with Pandora, those for Azure NetApp Files are not reliable, causing errors from time to time (and depending on the operation, very frequently) like this: + +```text +pmarques [ ~/go/src/github.com/hashicorp/terraform-provider-azurerm ]$ make acctests SERVICE='netapp' TESTARGS=' -parallel 5 -run=TestAccNetAppVolumeGroupSAPHana_crossRegionReplication -count=1' TESTTIMEOUT='1200m' +==> Checking that code complies with gofmt requirements... +==> Checking that Custom Timeouts are used... +egrep: warning: egrep is obsolescent; using grep -E +egrep: warning: egrep is obsolescent; using grep -E +==> Checking that acceptance test packages are used... +TF_ACC=1 go test -v ./internal/services/netapp -parallel 5 -run=TestAccNetAppVolumeGroupSAPHana_crossRegionReplication -count=1 -timeout 1200m -ldflags="-X=github.com/hashicorp/terraform-provider-azurerm/version.ProviderVersion=acc" +=== RUN TestAccNetAppVolumeGroupSAPHana_crossRegionReplication +=== PAUSE TestAccNetAppVolumeGroupSAPHana_crossRegionReplication +=== CONT TestAccNetAppVolumeGroupSAPHana_crossRegionReplication + testcase.go:173: Error running post-test destroy, there may be dangling resources: exit status 1 + + Error: deleting `volume`: deleting replicate Volume (Subscription: "66bc9830-19b6-4987-94d2-0e487be7aa47" + Resource Group Name: "acctestRG-netapp-241202215210177839" + Net App Account Name: "acctest-NetAppAccount-Secondary-241202215210177839" + Capacity Pool Name: "acctest-NetAppPool-Secondary-241202215210177839" + Volume Name: "acctest-NetAppVolume-1-Secondary-241202215210177839"): polling after VolumesDeleteReplication: `result.Status` was nil/empty - `op.Status` was "DeleteReplication" / `op.Properties.ProvisioningState` was "" + + deleting `volume`: deleting replicate Volume (Subscription: + "66bc9830-19b6-4987-94d2-0e487be7aa47" + Resource Group Name: "acctestRG-netapp-241202215210177839" + Net App Account Name: "acctest-NetAppAccount-Secondary-241202215210177839" + Capacity Pool Name: "acctest-NetAppPool-Secondary-241202215210177839" + Volume Name: "acctest-NetAppVolume-1-Secondary-241202215210177839"): polling + after VolumesDeleteReplication: `result.Status` was nil/empty - `op.Status` + was "DeleteReplication" / `op.Properties.ProvisioningState` was "" +--- FAIL: TestAccNetAppVolumeGroupSAPHana_crossRegionReplication (1375.67s) +FAIL +FAIL github.com/hashicorp/terraform-provider-azurerm/internal/services/netapp 1375.697s +FAIL +make: *** [GNUmakefile:103: acctests] Error 1 +``` + ## Data loss prevention protection - Due to possibility of a volume to be deleted due to configuration changes on config file or changes made outside of Terraform, we have decided to not allow deletion of volumes by default. This is to prevent data loss. If you want to delete a volume, you need to set the feature block configuration `prevent_deletion_if_contains_resources` argument to `true`. @@ -51,3 +89,53 @@ features { } } ``` + +## Azure NetApp Files has features that requires disassociation, e.g. BackupPolicyId and SnapshotPolicyIds + +- For cases where a property must have its content removed, mostly Ids (BackupPolicyId or SnapshotPolicyIds), instead of using `nil`, use `pointer.To("")`, this will trigger ANF RP to update the resource and set the value to empty string, setting as `nil` won't trigger any action within ANF RP. + +E.g. + +```golang +// Removing SnapshotId +update := volumes.VolumePatch{ + Properties: &volumes.VolumePatchProperties{ + DataProtection: &volumes.VolumePatchPropertiesDataProtection{ + Snapshot: &volumes.VolumeSnapshotProperties{ + SnapshotPolicyId: pointer.To(""), + }, + }, + }, +} +``` + +```golang +// Removing BackupPolicyId +backupPolicyIdRemoval := volumes.VolumePatch{ + Properties: &volumes.VolumePatchProperties{ + DataProtection: &volumes.VolumePatchPropertiesDataProtection{ + Backup: &volumes.VolumeBackupProperties{ + BackupPolicyId: pointer.To(""), + }, + }, + }, +} +``` + +## `Computed` attribute of a few configuration items + +- Azure NetApp Files resources are complex enough on the backend and a few configuration items must remain in `computed = true` state and not changed under any circumstance, otherwise, if it is a `ForceNew = true` type of configuration, it may result in data loss. The best example of this is the configuration called `network_features`, this must be `computed = true` at all times, Azure NetApp Files team will soon make changes to networking that will make the property `network_features` be changed from `basic` to `standard` and to prevent data loss, this configuration should stay as is, with `computed = true` set. + +```golang +"network_features": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + Default: string(volumes.NetworkFeaturesBasic), + ValidateFunc: validation.StringInSlice([]string{ + string(volumes.NetworkFeaturesBasic), + string(volumes.NetworkFeaturesStandard), + }, false), +}, +``` + diff --git a/internal/services/netapp/models/models.go b/internal/services/netapp/models/models.go index 8637074b2efb0..62feef05599cc 100644 --- a/internal/services/netapp/models/models.go +++ b/internal/services/netapp/models/models.go @@ -21,7 +21,7 @@ type NetAppAccountEncryptionDataSourceModel struct { EncryptionKey string `tfschema:"encryption_key"` } -type NetAppVolumeGroupVolume struct { +type NetAppVolumeGroupSAPHanaVolume struct { Id string `tfschema:"id"` Name string `tfschema:"name"` VolumePath string `tfschema:"volume_path"` @@ -42,24 +42,68 @@ type NetAppVolumeGroupVolume struct { DataProtectionSnapshotPolicy []DataProtectionSnapshotPolicy `tfschema:"data_protection_snapshot_policy"` } -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"` -} - -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"` +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 []NetAppVolumeGroupSAPHanaVolume `tfschema:"volume"` +} + +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 []NetAppVolumeGroupSAPHanaVolume `tfschema:"volume"` +} + +type NetAppVolumeGroupOracleVolume 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"` + DataProtectionSnapshotPolicy []DataProtectionSnapshotPolicy `tfschema:"data_protection_snapshot_policy"` + Zone string `tfschema:"zone"` + EncryptionKeySource string `tfschema:"encryption_key_source"` + KeyVaultPrivateEndpointId string `tfschema:"key_vault_private_endpoint_id"` + NetworkFeatures string `tfschema:"network_features"` +} + +type NetAppVolumeGroupOracleModel 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 []NetAppVolumeGroupOracleVolume `tfschema:"volume"` +} + +type NetAppVolumeGroupOracleDataSourceModel 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 []NetAppVolumeGroupOracleVolume `tfschema:"volume"` } type ExportPolicyRule struct { diff --git a/internal/services/netapp/netapp_account_encryption_resource_test.go b/internal/services/netapp/netapp_account_encryption_resource_test.go index 0fa754abc3958..473d69b92d996 100644 --- a/internal/services/netapp/netapp_account_encryption_resource_test.go +++ b/internal/services/netapp/netapp_account_encryption_resource_test.go @@ -106,6 +106,20 @@ func (r NetAppAccountEncryptionResource) cmkSystemAssigned(data acceptance.TestD data "azurerm_client_config" "current" { } +resource "azurerm_netapp_account" "test" { + name = "acctest-NetAppAccount-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + identity { + type = "SystemAssigned" + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z" + } +} + resource "azurerm_key_vault" "test" { name = "anfakv%[2]d" location = azurerm_resource_group.test.location @@ -115,24 +129,43 @@ resource "azurerm_key_vault" "test" { enabled_for_template_deployment = true purge_protection_enabled = true tenant_id = "%[3]s" + sku_name = "standard" - sku_name = "standard" -} + access_policy { + tenant_id = azurerm_netapp_account.test.identity.0.tenant_id + object_id = data.azurerm_client_config.current.object_id -resource "azurerm_key_vault_access_policy" "test-currentuser" { - key_vault_id = azurerm_key_vault.test.id - tenant_id = azurerm_netapp_account.test.identity.0.tenant_id - object_id = data.azurerm_client_config.current.object_id - - key_permissions = [ - "Get", - "Create", - "Delete", - "WrapKey", - "UnwrapKey", - "GetRotationPolicy", - "SetRotationPolicy", - ] + certificate_permissions = [] + secret_permissions = [] + storage_permissions = [] + key_permissions = [ + "Get", + "Create", + "Delete", + "WrapKey", + "UnwrapKey", + "GetRotationPolicy", + "SetRotationPolicy", + ] + } + + access_policy { + tenant_id = azurerm_netapp_account.test.identity.0.tenant_id + object_id = azurerm_netapp_account.test.identity.0.principal_id + + certificate_permissions = [] + secret_permissions = [] + storage_permissions = [] + key_permissions = [ + "Get", + "Encrypt", + "Decrypt" + ] + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z" + } } resource "azurerm_key_vault_key" "test" { @@ -149,44 +182,12 @@ resource "azurerm_key_vault_key" "test" { "verify", "wrapKey", ] - - depends_on = [ - azurerm_key_vault_access_policy.test-currentuser - ] -} - -resource "azurerm_netapp_account" "test" { - name = "acctest-NetAppAccount-%[2]d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - - identity { - type = "SystemAssigned" - } -} - -resource "azurerm_key_vault_access_policy" "test-systemassigned" { - key_vault_id = azurerm_key_vault.test.id - tenant_id = azurerm_netapp_account.test.identity.0.tenant_id - object_id = azurerm_netapp_account.test.identity.0.principal_id - - key_permissions = [ - "Get", - "Encrypt", - "Decrypt" - ] } resource "azurerm_netapp_account_encryption" "test" { - netapp_account_id = azurerm_netapp_account.test.id - + netapp_account_id = azurerm_netapp_account.test.id system_assigned_identity_principal_id = azurerm_netapp_account.test.identity.0.principal_id - - encryption_key = azurerm_key_vault_key.test.versionless_id - - depends_on = [ - azurerm_key_vault_access_policy.test-systemassigned - ] + encryption_key = azurerm_key_vault_key.test.versionless_id } `, r.template(data), data.RandomInteger, tenantID) } @@ -199,6 +200,10 @@ resource "azurerm_user_assigned_identity" "test" { name = "user-assigned-identity-%[2]d" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z" + } } data "azurerm_client_config" "current" { @@ -213,13 +218,15 @@ resource "azurerm_key_vault" "test" { enabled_for_template_deployment = true purge_protection_enabled = true tenant_id = "%[3]s" - - sku_name = "standard" + sku_name = "standard" access_policy { tenant_id = "%[3]s" object_id = data.azurerm_client_config.current.object_id + certificate_permissions = [] + secret_permissions = [] + storage_permissions = [] key_permissions = [ "Get", "Create", @@ -235,12 +242,19 @@ resource "azurerm_key_vault" "test" { tenant_id = "%[3]s" object_id = azurerm_user_assigned_identity.test.principal_id + certificate_permissions = [] + secret_permissions = [] + storage_permissions = [] key_permissions = [ "Get", "Encrypt", "Decrypt" ] } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z" + } } resource "azurerm_key_vault_key" "test" { @@ -270,14 +284,17 @@ resource "azurerm_netapp_account" "test" { azurerm_user_assigned_identity.test.id ] } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } } resource "azurerm_netapp_account_encryption" "test" { - netapp_account_id = azurerm_netapp_account.test.id - + netapp_account_id = azurerm_netapp_account.test.id user_assigned_identity_id = azurerm_user_assigned_identity.test.id - - encryption_key = azurerm_key_vault_key.test.versionless_id + encryption_key = azurerm_key_vault_key.test.versionless_id } `, r.template(data), data.RandomInteger, tenantID) } @@ -289,6 +306,21 @@ func (r NetAppAccountEncryptionResource) keyUpdate1(data acceptance.TestData, te data "azurerm_client_config" "current" { } +resource "azurerm_netapp_account" "test" { + name = "acctest-NetAppAccount-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + identity { + type = "SystemAssigned" + } + + tags = { + "SkipNRMSNSG" = "true", + "CreatedOnDate" = "2022-07-08T23:50:21Z" + } +} + resource "azurerm_key_vault" "test" { name = "anfakv%[2]d" location = azurerm_resource_group.test.location @@ -298,24 +330,43 @@ resource "azurerm_key_vault" "test" { enabled_for_template_deployment = true purge_protection_enabled = true tenant_id = "%[3]s" + sku_name = "standard" - sku_name = "standard" -} + access_policy { + tenant_id = azurerm_netapp_account.test.identity.0.tenant_id + object_id = data.azurerm_client_config.current.object_id -resource "azurerm_key_vault_access_policy" "test-currentuser" { - key_vault_id = azurerm_key_vault.test.id - tenant_id = azurerm_netapp_account.test.identity.0.tenant_id - object_id = data.azurerm_client_config.current.object_id - - key_permissions = [ - "Get", - "Create", - "Delete", - "WrapKey", - "UnwrapKey", - "GetRotationPolicy", - "SetRotationPolicy", - ] + certificate_permissions = [] + secret_permissions = [] + storage_permissions = [] + key_permissions = [ + "Get", + "Create", + "Delete", + "WrapKey", + "UnwrapKey", + "GetRotationPolicy", + "SetRotationPolicy", + ] + } + + access_policy { + tenant_id = azurerm_netapp_account.test.identity.0.tenant_id + object_id = azurerm_netapp_account.test.identity.0.principal_id + + certificate_permissions = [] + secret_permissions = [] + storage_permissions = [] + key_permissions = [ + "Get", + "Encrypt", + "Decrypt" + ] + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z" + } } resource "azurerm_key_vault_key" "test" { @@ -332,10 +383,6 @@ resource "azurerm_key_vault_key" "test" { "verify", "wrapKey", ] - - depends_on = [ - azurerm_key_vault_access_policy.test-currentuser - ] } resource "azurerm_key_vault_key" "test-new-key" { @@ -352,45 +399,12 @@ resource "azurerm_key_vault_key" "test-new-key" { "verify", "wrapKey", ] - - depends_on = [ - azurerm_key_vault_key.test, - azurerm_key_vault_access_policy.test-currentuser - ] -} - -resource "azurerm_netapp_account" "test" { - name = "acctest-NetAppAccount-%[2]d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - - identity { - type = "SystemAssigned" - } -} - -resource "azurerm_key_vault_access_policy" "test-systemassigned" { - key_vault_id = azurerm_key_vault.test.id - tenant_id = azurerm_netapp_account.test.identity.0.tenant_id - object_id = azurerm_netapp_account.test.identity.0.principal_id - - key_permissions = [ - "Get", - "Encrypt", - "Decrypt" - ] } resource "azurerm_netapp_account_encryption" "test" { - netapp_account_id = azurerm_netapp_account.test.id - + netapp_account_id = azurerm_netapp_account.test.id system_assigned_identity_principal_id = azurerm_netapp_account.test.identity.0.principal_id - - encryption_key = azurerm_key_vault_key.test.versionless_id - - depends_on = [ - azurerm_key_vault_access_policy.test-systemassigned - ] + encryption_key = azurerm_key_vault_key.test.versionless_id } `, r.template(data), data.RandomInteger, tenantID) } @@ -402,6 +416,21 @@ func (r NetAppAccountEncryptionResource) keyUpdate2(data acceptance.TestData, te data "azurerm_client_config" "current" { } +resource "azurerm_netapp_account" "test" { + name = "acctest-NetAppAccount-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + identity { + type = "SystemAssigned" + } + + tags = { + "SkipNRMSNSG" = "true", + "CreatedOnDate" = "2022-07-08T23:50:21Z" + } +} + resource "azurerm_key_vault" "test" { name = "anfakv%[2]d" location = azurerm_resource_group.test.location @@ -411,24 +440,43 @@ resource "azurerm_key_vault" "test" { enabled_for_template_deployment = true purge_protection_enabled = true tenant_id = "%[3]s" + sku_name = "standard" - sku_name = "standard" -} + access_policy { + tenant_id = azurerm_netapp_account.test.identity.0.tenant_id + object_id = data.azurerm_client_config.current.object_id -resource "azurerm_key_vault_access_policy" "test-currentuser" { - key_vault_id = azurerm_key_vault.test.id - tenant_id = azurerm_netapp_account.test.identity.0.tenant_id - object_id = data.azurerm_client_config.current.object_id - - key_permissions = [ - "Get", - "Create", - "Delete", - "WrapKey", - "UnwrapKey", - "GetRotationPolicy", - "SetRotationPolicy", - ] + certificate_permissions = [] + secret_permissions = [] + storage_permissions = [] + key_permissions = [ + "Get", + "Create", + "Delete", + "WrapKey", + "UnwrapKey", + "GetRotationPolicy", + "SetRotationPolicy", + ] + } + + access_policy { + tenant_id = azurerm_netapp_account.test.identity.0.tenant_id + object_id = azurerm_netapp_account.test.identity.0.principal_id + + certificate_permissions = [] + secret_permissions = [] + storage_permissions = [] + key_permissions = [ + "Get", + "Encrypt", + "Decrypt" + ] + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z" + } } resource "azurerm_key_vault_key" "test" { @@ -445,10 +493,6 @@ resource "azurerm_key_vault_key" "test" { "verify", "wrapKey", ] - - depends_on = [ - azurerm_key_vault_access_policy.test-currentuser - ] } resource "azurerm_key_vault_key" "test-new-key" { @@ -467,43 +511,14 @@ resource "azurerm_key_vault_key" "test-new-key" { ] depends_on = [ - azurerm_key_vault_key.test, - azurerm_key_vault_access_policy.test-currentuser - ] -} - -resource "azurerm_netapp_account" "test" { - name = "acctest-NetAppAccount-%[2]d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - - identity { - type = "SystemAssigned" - } -} - -resource "azurerm_key_vault_access_policy" "test-systemassigned" { - key_vault_id = azurerm_key_vault.test.id - tenant_id = azurerm_netapp_account.test.identity.0.tenant_id - object_id = azurerm_netapp_account.test.identity.0.principal_id - - key_permissions = [ - "Get", - "Encrypt", - "Decrypt" + azurerm_key_vault_key.test ] } resource "azurerm_netapp_account_encryption" "test" { - netapp_account_id = azurerm_netapp_account.test.id - + netapp_account_id = azurerm_netapp_account.test.id system_assigned_identity_principal_id = azurerm_netapp_account.test.identity.0.principal_id - - encryption_key = azurerm_key_vault_key.test-new-key.versionless_id - - depends_on = [ - azurerm_key_vault_access_policy.test-systemassigned - ] + encryption_key = azurerm_key_vault_key.test-new-key.versionless_id } `, r.template(data), data.RandomInteger, tenantID) } @@ -520,6 +535,11 @@ provider "azurerm" { purge_soft_delete_on_destroy = false purge_soft_deleted_keys_on_destroy = false } + + netapp { + prevent_volume_destruction = false + delete_backups_on_backup_vault_destroy = true + } } } @@ -528,7 +548,9 @@ resource "azurerm_resource_group" "test" { location = "%[2]s" tags = { - "SkipNRMSNSG" = "true" + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true", + "SkipNRMSNSG" = "true" } } `, data.RandomInteger, data.Locations.Primary) diff --git a/internal/services/netapp/netapp_snapshot_policy_resource.go b/internal/services/netapp/netapp_snapshot_policy_resource.go index 2a0511a5c7278..400e8b149bda5 100644 --- a/internal/services/netapp/netapp_snapshot_policy_resource.go +++ b/internal/services/netapp/netapp_snapshot_policy_resource.go @@ -11,13 +11,17 @@ import ( "strings" "time" + "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/go-azure-helpers/lang/response" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2024-03-01/capacitypools" "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2024-03-01/snapshotpolicy" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2024-03-01/volumes" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/locks" 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" @@ -340,6 +344,7 @@ func resourceNetAppSnapshotPolicyRead(d *pluginsdk.ResourceData, meta interface{ func resourceNetAppSnapshotPolicyDelete(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).NetApp.SnapshotPoliciesClient + volumeClient := meta.(*clients.Client).NetApp.VolumeClient ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() @@ -348,12 +353,60 @@ func resourceNetAppSnapshotPolicyDelete(d *pluginsdk.ResourceData, meta interfac return err } - // Deleting snapshot policy and waiting for it fo fully complete the operation - if err = client.SnapshotPoliciesDeleteThenPoll(ctx, *id); err != nil { - return fmt.Errorf("deleting %s: %+v", id, err) + // Try to delete the snapshot policy using DeleteThenPoll + err = client.SnapshotPoliciesDeleteThenPoll(ctx, *id) + if err != nil { + // Check if error is about snapshot policy being in use + if strings.Contains(err.Error(), "SnapshotPolicy is used") { + // Get all volumes in the account that might be using this snapshot policy + volumeIds, err := findVolumesUsingSnapshotPolicy(ctx, meta.(*clients.Client), *id) + if err != nil { + return fmt.Errorf("finding volumes using snapshot policy %s: %+v", *id, err) + } + + // Disassociate snapshot policy from each volume + for _, volumeId := range volumeIds { + volId, err := volumes.ParseVolumeID(volumeId) + if err != nil { + return fmt.Errorf("parsing volume ID %q: %+v", volumeId, err) + } + + // Update volume to remove snapshot policy + update := volumes.VolumePatch{ + Properties: &volumes.VolumePatchProperties{ + DataProtection: &volumes.VolumePatchPropertiesDataProtection{ + Snapshot: &volumes.VolumeSnapshotProperties{ + SnapshotPolicyId: pointer.To(""), + }, + }, + }, + } + + locks.ByID(volumeId) + + if err = volumeClient.UpdateThenPoll(ctx, *volId, update); err != nil { + locks.UnlockByID(volumeId) + return fmt.Errorf("removing snapshot policy from volume %s: %+v", *volId, err) + } + + // Wait for the update to complete + if err := waitForVolumeCreateOrUpdate(ctx, volumeClient, *volId); err != nil { + locks.UnlockByID(volumeId) + return fmt.Errorf("waiting for snapshot policy removal from volume %s: %+v", *volId, err) + } + + locks.UnlockByID(volumeId) + } + + // Try deleting the snapshot policy again + if err = client.SnapshotPoliciesDeleteThenPoll(ctx, *id); err != nil { + return fmt.Errorf("deleting %s after volume disassociation: %+v", *id, err) + } + } else { + return fmt.Errorf("deleting %s: %+v", *id, err) + } } - log.Printf("[DEBUG] Waiting for %s to be deleted", id) if err := waitForSnapshotPolicyDeletion(ctx, client, *id, d.Timeout(pluginsdk.TimeoutDelete)); err != nil { return err } @@ -361,6 +414,51 @@ func resourceNetAppSnapshotPolicyDelete(d *pluginsdk.ResourceData, meta interfac return nil } +func findVolumesUsingSnapshotPolicy(ctx context.Context, client *clients.Client, snapshotPolicyId snapshotpolicy.SnapshotPolicyId) ([]string, error) { + volumeIds := make([]string, 0) + poolClient := client.NetApp.PoolClient + accountId := capacitypools.NewNetAppAccountID(snapshotPolicyId.SubscriptionId, snapshotPolicyId.ResourceGroupName, snapshotPolicyId.NetAppAccountName) + + poolsResult, err := poolClient.PoolsList(ctx, accountId) + if err != nil { + return nil, fmt.Errorf("listing capacity pools in account %s: %+v", snapshotPolicyId.NetAppAccountName, err) + } + + if model := poolsResult.Model; model != nil { + volumeClient := client.NetApp.VolumeClient + + for _, pool := range *model { + if pool.Name == nil { + continue + } + + poolNameParts := strings.Split(pointer.From(pool.Name), "/") + poolName := poolNameParts[len(poolNameParts)-1] + volumeId := volumes.NewCapacityPoolID(snapshotPolicyId.SubscriptionId, snapshotPolicyId.ResourceGroupName, snapshotPolicyId.NetAppAccountName, poolName) + + volumesResult, err := volumeClient.List(ctx, volumeId) + if err != nil { + return nil, fmt.Errorf("listing volumes in pool %s: %+v", poolName, err) + } + + if volumesModel := volumesResult.Model; volumesModel != nil { + for _, volume := range *volumesModel { + if volume.Id == nil || volume.Properties.DataProtection == nil || + volume.Properties.DataProtection.Snapshot == nil || volume.Properties.DataProtection.Snapshot.SnapshotPolicyId == nil { + continue + } + + if strings.EqualFold(*volume.Properties.DataProtection.Snapshot.SnapshotPolicyId, snapshotPolicyId.ID()) { + volumeIds = append(volumeIds, *volume.Id) + } + } + } + } + } + + return volumeIds, nil +} + func expandNetAppSnapshotPolicyHourlySchedule(input []interface{}) *snapshotpolicy.HourlySchedule { if len(input) == 0 || input[0] == nil { return &snapshotpolicy.HourlySchedule{} diff --git a/internal/services/netapp/netapp_volume_group_oracle_data_source.go b/internal/services/netapp/netapp_volume_group_oracle_data_source.go new file mode 100644 index 0000000000000..fb6271654fddb --- /dev/null +++ b/internal/services/netapp/netapp_volume_group_oracle_data_source.go @@ -0,0 +1,298 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package netapp + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2024-03-01/volumegroups" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + netAppModels "github.com/hashicorp/terraform-provider-azurerm/internal/services/netapp/models" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/netapp/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +var _ sdk.DataSource = NetAppVolumeGroupOracleDataSource{} + +type NetAppVolumeGroupOracleDataSource struct{} + +func (r NetAppVolumeGroupOracleDataSource) ResourceType() string { + return "azurerm_netapp_volume_group_oracle" +} + +func (r NetAppVolumeGroupOracleDataSource) ModelObject() interface{} { + return &netAppModels.NetAppVolumeGroupOracleDataSourceModel{} +} + +func (r NetAppVolumeGroupOracleDataSource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return volumegroups.ValidateVolumeGroupID +} + +func (r NetAppVolumeGroupOracleDataSource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.VolumeGroupName, + }, + + "resource_group_name": commonschema.ResourceGroupName(), + + "account_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.AccountName, + }, + } +} + +func (r NetAppVolumeGroupOracleDataSource) 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, + }, + + "zone": commonschema.ZoneSingleComputed(), + + "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, + }, + }, + }, + }, + + "encryption_key_source": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "key_vault_private_endpoint_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "network_features": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + } +} + +func (r NetAppVolumeGroupOracleDataSource) 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 netAppModels.NetAppVolumeGroupOracleDataSourceModel + 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.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s was not found", id) + } + return fmt.Errorf("retrieving %s: %v", id, err) + } + + if model := resp.Model; model != nil { + state.Location = location.Normalize(pointer.From(model.Location)) + if props := model.Properties; props != nil { + if groupMetaData := props.GroupMetaData; groupMetaData != nil { + state.ApplicationIdentifier = pointer.From(groupMetaData.ApplicationIdentifier) + state.GroupDescription = pointer.From(groupMetaData.GroupDescription) + } + + volumes, err := flattenNetAppVolumeGroupOracleVolumes(ctx, props.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_oracle_data_source_test.go b/internal/services/netapp/netapp_volume_group_oracle_data_source_test.go new file mode 100644 index 0000000000000..f44f5a714da13 --- /dev/null +++ b/internal/services/netapp/netapp_volume_group_oracle_data_source_test.go @@ -0,0 +1,42 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package netapp_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type NetAppVolumeGroupOracleDataSource struct{} + +func TestAccNetAppVolumeGroupOracleDataSource_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_netapp_volume_group_oracle", "test") + d := NetAppVolumeGroupOracleDataSource{} + + 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("ora-log"), + ), + }, + }) +} + +func (d NetAppVolumeGroupOracleDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_netapp_volume_group_oracle" "test" { + name = azurerm_netapp_volume_group_oracle.test.name + resource_group_name = azurerm_netapp_volume_group_oracle.test.resource_group_name + account_name = azurerm_netapp_volume_group_oracle.test.account_name +} +`, NetAppVolumeGroupOracleResource{}.basicAvailabilityZone(data)) +} diff --git a/internal/services/netapp/netapp_volume_group_oracle_resource.go b/internal/services/netapp/netapp_volume_group_oracle_resource.go new file mode 100644 index 0000000000000..2510ef59916d2 --- /dev/null +++ b/internal/services/netapp/netapp_volume_group_oracle_resource.go @@ -0,0 +1,550 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package netapp + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2024-03-01/capacitypools" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2024-03-01/volumegroups" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2024-03-01/volumes" + "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + netAppModels "github.com/hashicorp/terraform-provider-azurerm/internal/services/netapp/models" + 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 NetAppVolumeGroupOracleResource struct{} + +var _ sdk.Resource = NetAppVolumeGroupOracleResource{} + +func (r NetAppVolumeGroupOracleResource) ModelObject() interface{} { + return &netAppModels.NetAppVolumeGroupOracleModel{} +} + +func (r NetAppVolumeGroupOracleResource) ResourceType() string { + return "azurerm_netapp_volume_group_oracle" +} + +func (r NetAppVolumeGroupOracleResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return volumegroups.ValidateVolumeGroupID +} + +func (r NetAppVolumeGroupOracleResource) 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: 12, + 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, + }, + + "zone": commonschema.ZoneSingleOptionalForceNew(), + + "volume_spec_name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(netAppValidate.PossibleValuesForVolumeSpecNameOracle(), 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(netAppValidate.PossibleValuesForProtocolTypeVolumeGroupOracle(), false), + }, + }, + + "security_style": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(volumegroups.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, + }, + + "network_features": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, // O+C - This is Optional/Computed because the service team is changing network features on the backend to upgrade everyone from Basic to Standard and there is a feature that allows customers to change network features from portal but not the API. This could cause drift that forces data loss that we want to avoid + ValidateFunc: validation.StringInSlice(volumegroups.PossibleValuesForNetworkFeatures(), false), + }, + + "mount_ip_addresses": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "data_protection_snapshot_policy": { + Type: pluginsdk.TypeList, + Optional: true, + Computed: true, // O+C - Adding this because Terraform is not being able to build proper deletion graph, it is trying to delete the snapshot policy before the volume because this is in a deeper level within the schema inside an array of volumes + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "snapshot_policy_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + }, + }, + }, + + "encryption_key_source": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + Computed: true, // O+C - This is computed/optional since there is a feature coming up that will allow customers to change the encryption key source from portal but not the API. This could cause drift if configuration is not updated + ValidateFunc: validation.StringInSlice(volumes.PossibleValuesForEncryptionKeySource(), false), + }, + + "key_vault_private_endpoint_id": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + Computed: true, // O+C - This is computed/optional since there is a feature coming up that will allow customers to change the encryption key source from portal but not the API (tied to encryption_key_source). This could cause drift if configuration is not updated + ValidateFunc: azure.ValidateResourceID, + }, + }, + }, + }, + } +} + +func (r NetAppVolumeGroupOracleResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (r NetAppVolumeGroupOracleResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 90 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.NetApp.VolumeGroupClient + subscriptionId := metadata.Client.Account.SubscriptionId + + var model netAppModels.NetAppVolumeGroupOracleModel + 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.Get(ctx, id) + if err != nil && !response.WasNotFound(existing.HttpResponse) { + 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 := expandNetAppVolumeGroupOracleVolumes(model.Volumes) + if err != nil { + return err + } + + // Performing some basic validations that are not possible in the schema + if errorList := netAppValidate.ValidateNetAppVolumeGroupOracleVolumes(volumeList); len(errorList) > 0 { + return fmt.Errorf("one or more issues found while performing deeper validations for %s:\n%+v", id, errorList) + } + + 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.ApplicationTypeORACLE), + ApplicationIdentifier: utils.String(model.ApplicationIdentifier), + }, + Volumes: volumeList, + }, + } + + if err = client.CreateThenPoll(ctx, id, parameters); 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 fmt.Errorf("waiting creation %s: %+v", id, err) + } + + metadata.SetID(id) + + return nil + }, + } +} + +func (r NetAppVolumeGroupOracleResource) 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 netAppModels.NetAppVolumeGroupOracleModel + if err := metadata.Decode(&state); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + metadata.Logger.Infof("Updating %s", id) + + if metadata.ResourceData.HasChange("volume") { + 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, netAppValidate.ValidateNetAppVolumeGroupExportPolicyRule(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)) { + 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) + } + + // Waiting for volume to fully complete an update + if err := waitForVolumeCreateOrUpdate(ctx, volumeClient, volumeId); err != nil { + return fmt.Errorf("waiting update %s: %+v", volumeId, err) + } + } + } + } + + return nil + }, + } +} + +func (r NetAppVolumeGroupOracleResource) 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 netAppModels.NetAppVolumeGroupOracleModel + if err := metadata.Decode(&state); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + existing, err := client.Get(ctx, pointer.From(id)) + if err != nil { + if response.WasNotFound(existing.HttpResponse) { + return metadata.MarkAsGone(id) + } + return fmt.Errorf("retrieving %s: %v", id, err) + } + + model := netAppModels.NetAppVolumeGroupOracleModel{ + Name: id.VolumeGroupName, + AccountName: id.NetAppAccountName, + Location: location.NormalizeNilable(existing.Model.Location), + ResourceGroupName: id.ResourceGroupName, + } + + if props := existing.Model.Properties; props != nil { + model.GroupDescription = pointer.From(props.GroupMetaData.GroupDescription) + model.ApplicationIdentifier = pointer.From(props.GroupMetaData.ApplicationIdentifier) + + volumes, err := flattenNetAppVolumeGroupOracleVolumes(ctx, props.Volumes, metadata) + if err != nil { + return fmt.Errorf("setting `volume`: %+v", err) + } + + model.Volumes = volumes + } + + metadata.SetID(id) + + return metadata.Encode(&model) + }, + } +} + +func (r NetAppVolumeGroupOracleResource) 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.Get(ctx, pointer.From(id)) + if err != nil { + if response.WasNotFound(existing.HttpResponse) { + 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) + } + } + } + } + + if err = client.DeleteThenPoll(ctx, pointer.From(id)); err != nil { + return fmt.Errorf("deleting %s: %+v", pointer.From(id), err) + } + + // Waiting for volume group be completely deleted + if err := waitForVolumeGroupDelete(ctx, client, pointer.From(id)); err != nil { + return fmt.Errorf("waiting delete %s: %+v", pointer.From(id), err) + } + + return nil + }, + } +} diff --git a/internal/services/netapp/netapp_volume_group_oracle_resource_test.go b/internal/services/netapp/netapp_volume_group_oracle_resource_test.go new file mode 100644 index 0000000000000..1c85185dc30b3 --- /dev/null +++ b/internal/services/netapp/netapp_volume_group_oracle_resource_test.go @@ -0,0 +1,1209 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package netapp_test + +import ( + "context" + "fmt" + "os" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2024-03-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 NetAppVolumeGroupOracleResource struct{} + +func TestAccNetAppVolumeGroupOracle_basicAvailabilityZone(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_oracle", "test") + r := NetAppVolumeGroupOracleResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basicAvailabilityZone(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccNetAppVolumeGroupOracle_basicProximityPlacementGroup(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_oracle", "test") + r := NetAppVolumeGroupOracleResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basicProximityPlacementGroup(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccNetAppVolumeGroupOracle_nfsv3(t *testing.T) { + // Adjust test configurations to exclude data_protection_replication + // Use the new NetAppVolumeGroupOracleVolume model in test data + data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_oracle", "test") + r := NetAppVolumeGroupOracleResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.nfsv3(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccNetAppVolumeGroupOracle_snapshotPolicy(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_oracle", "test") + r := NetAppVolumeGroupOracleResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.avgSnapshotPolicy(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccNetAppVolumeGroupOracle_snapshotPolicyUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_oracle", "test") + r := NetAppVolumeGroupOracleResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.avgSnapshotPolicy(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 TestAccNetAppVolumeGroupOracle_volumeUpdates(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_oracle", "test") + r := NetAppVolumeGroupOracleResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basicAvailabilityZone(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"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccNetAppVolumeGroupOracle_volCustomerManagedKeyEncryption(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_oracle", "test") + r := NetAppVolumeGroupOracleResource{} + + tenantID := os.Getenv("ARM_TENANT_ID") + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.volEncryptionCmkOracle(data, tenantID), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (t NetAppVolumeGroupOracleResource) 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.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving %s: %+v", id, err) + } + + return utils.Bool(true), nil +} + +func (NetAppVolumeGroupOracleResource) basicAvailabilityZone(data acceptance.TestData) string { + template := NetAppVolumeGroupOracleResource{}.templateAvailabilityZoneOracle(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_netapp_volume_group_oracle" "test" { + name = "acctest-NetAppVolumeGroupOracle-%[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 for Oracle" + application_identifier = "TST" + + volume { + name = "acctest-NetAppVolume-Ora1-%[2]d" + volume_path = "my-unique-file-ora-path-1-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + zone = "1" + volume_spec_name = "ora-data1" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "unix" + snapshot_directory_visible = false + network_features = "Standard" + + 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-OraLog-%[2]d" + volume_path = "my-unique-file-oralog-path-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + zone = "1" + volume_spec_name = "ora-log" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "unix" + snapshot_directory_visible = false + network_features = "Standard" + + 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" + } + } +} +`, template, data.RandomInteger) +} + +func (NetAppVolumeGroupOracleResource) basicProximityPlacementGroup(data acceptance.TestData) string { + template := NetAppVolumeGroupOracleResource{}.templatePpgOracle(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_netapp_volume_group_oracle" "test" { + name = "acctest-NetAppVolumeGroupOracle-%[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 for Oracle" + application_identifier = "TST" + + volume { + name = "acctest-NetAppVolume-Ora1-%[2]d" + volume_path = "my-unique-file-ora-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 = "ora-data1" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "unix" + snapshot_directory_visible = false + network_features = "Basic" + + 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-OraLog-%[2]d" + volume_path = "my-unique-file-oralog-path-%[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 = "ora-log" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "unix" + snapshot_directory_visible = false + network_features = "Basic" + + 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 (NetAppVolumeGroupOracleResource) nfsv3(data acceptance.TestData) string { + template := NetAppVolumeGroupOracleResource{}.templateAvailabilityZoneOracle(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_netapp_volume_group_oracle" "test" { + name = "acctest-NetAppVolumeGroupOracle-%[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 for Oracle" + application_identifier = "TST" + + volume { + name = "acctest-NetAppVolume-Ora1-%[2]d" + volume_path = "my-unique-file-ora-path-1-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + zone = "1" + volume_spec_name = "ora-data1" + 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-OraLog-%[2]d" + volume_path = "my-unique-file-oralog-path-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + zone = "1" + volume_spec_name = "ora-log" + 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" + } + } +} +`, template, data.RandomInteger) +} + +func (NetAppVolumeGroupOracleResource) avgSnapshotPolicy(data acceptance.TestData) string { + template := NetAppVolumeGroupOracleResource{}.templateAvailabilityZoneOracle(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_oracle" "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-Ora1-%[2]d" + volume_path = "my-unique-file-ora-path-1-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + zone = "1" + volume_spec_name = "ora-data1" + 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-OraLog-%[2]d" + volume_path = "my-unique-file-ora-path-2-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + zone = "1" + volume_spec_name = "ora-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" + } + } + + depends_on = [ + azurerm_netapp_snapshot_policy.test + ] +} +`, template, data.RandomInteger) +} + +func (NetAppVolumeGroupOracleResource) updateAvgSnapshotPolicy(data acceptance.TestData) string { + template := NetAppVolumeGroupOracleResource{}.templateAvailabilityZoneOracle(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_oracle" "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-Ora1-%[2]d" + volume_path = "my-unique-file-ora-path-1-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + zone = "1" + volume_spec_name = "ora-data1" + 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-OraLog-%[2]d" + volume_path = "my-unique-file-ora-path-2-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + zone = "1" + volume_spec_name = "ora-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" + } + } + + depends_on = [ + azurerm_netapp_snapshot_policy.test + ] +} +`, template, data.RandomInteger) +} + +func (NetAppVolumeGroupOracleResource) updateVolumes(data acceptance.TestData) string { + template := NetAppVolumeGroupOracleResource{}.templateAvailabilityZoneOracle(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_netapp_volume_group_oracle" "test" { + name = "acctest-NetAppVolumeGroupOracle-%[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 for Oracle" + application_identifier = "TST" + + volume { + name = "acctest-NetAppVolume-Ora1-%[2]d" + volume_path = "my-unique-file-ora-path-1-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + zone = "1" + volume_spec_name = "ora-data1" + 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-OraLog-%[2]d" + volume_path = "my-unique-file-oralog-path-%[2]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test.id + zone = "1" + volume_spec_name = "ora-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" + } + } +} +`, template, data.RandomInteger) +} + +func (NetAppVolumeGroupOracleResource) volEncryptionCmkOracle(data acceptance.TestData, tenantID string) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + resource_group { + prevent_deletion_if_contains_resources = false + } + netapp { + prevent_volume_destruction = false + } + } +} + +data "azurerm_client_config" "current" { +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-netapp-%[1]d" + location = "%[3]s" + + tags = { + "SkipNRMSNSG" = "true", + "CreatedOnDate" = "2022-07-08T23:50:21Z" + } +} + +resource "azurerm_netapp_account" "test" { + name = "acctest-NetAppAccount-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + identity { + type = "SystemAssigned" + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z" + } +} + +resource "azurerm_key_vault" "test" { + name = "anfakv%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + enabled_for_disk_encryption = true + enabled_for_deployment = true + enabled_for_template_deployment = true + purge_protection_enabled = true + tenant_id = "%[2]s" + sku_name = "standard" + + access_policy { + tenant_id = azurerm_netapp_account.test.identity.0.tenant_id + object_id = data.azurerm_client_config.current.object_id + + certificate_permissions = [] + secret_permissions = [] + storage_permissions = [] + key_permissions = [ + "Get", + "Create", + "Delete", + "WrapKey", + "UnwrapKey", + "GetRotationPolicy", + "SetRotationPolicy", + ] + } + + access_policy { + tenant_id = azurerm_netapp_account.test.identity.0.tenant_id + object_id = azurerm_netapp_account.test.identity.0.principal_id + + certificate_permissions = [] + secret_permissions = [] + storage_permissions = [] + key_permissions = [ + "Get", + "Encrypt", + "Decrypt" + ] + } + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z" + } +} + +resource "azurerm_key_vault_key" "test" { + name = "anfenckey%[1]d" + key_vault_id = azurerm_key_vault.test.id + key_type = "RSA" + key_size = 2048 + + key_opts = [ + "decrypt", + "encrypt", + "sign", + "unwrapKey", + "verify", + "wrapKey", + ] +} + +resource "azurerm_netapp_account_encryption" "test" { + netapp_account_id = azurerm_netapp_account.test.id + system_assigned_identity_principal_id = azurerm_netapp_account.test.identity.0.principal_id + encryption_key = azurerm_key_vault_key.test.versionless_id +} + +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.88.0.0/16"] + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} + +resource "azurerm_subnet" "test-delegated" { + name = "acctest-Delegated-Subnet-%[1]d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.88.1.0/24"] + + delegation { + name = "testdelegation" + + service_delegation { + name = "Microsoft.Netapp/volumes" + actions = ["Microsoft.Network/networkinterfaces/*", "Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +resource "azurerm_subnet" "test-non-delegated" { + name = "acctest-Non-Delegated-Subnet-%[1]d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefixes = ["10.88.0.0/24"] +} + +resource "azurerm_private_endpoint" "test" { + name = "acctest-pe-akv-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + subnet_id = azurerm_subnet.test-non-delegated.id + + private_service_connection { + name = "acctest-pe-sc-akv-%[1]d" + private_connection_resource_id = azurerm_key_vault.test.id + is_manual_connection = false + subresource_names = ["Vault"] + } + + tags = { + CreatedOnDate = "2023-10-03T19:58:43.6509795Z" + } +} + +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 = 4 + qos_type = "Manual" + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } + + depends_on = [ + azurerm_netapp_account_encryption.test + ] +} + +resource "azurerm_netapp_volume_group_oracle" "test" { + name = "acctest-NetAppVolumeGroupOracle-%[1]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 for Oracle" + application_identifier = "TST" + + volume { + name = "acctest-NetAppVolume-Ora1-%[1]d" + volume_path = "my-unique-file-ora-path-1-%[1]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test-delegated.id + zone = "1" + volume_spec_name = "ora-data1" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "unix" + snapshot_directory_visible = false + encryption_key_source = "Microsoft.KeyVault" + key_vault_private_endpoint_id = azurerm_private_endpoint.test.id + network_features = "Standard" + + 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 + } + } + + volume { + name = "acctest-NetAppVolume-OraLog-%[1]d" + volume_path = "my-unique-file-oralog-path-%[1]d" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.test.id + subnet_id = azurerm_subnet.test-delegated.id + zone = "1" + volume_spec_name = "ora-log" + storage_quota_in_gb = 1024 + throughput_in_mibps = 24 + protocols = ["NFSv4.1"] + security_style = "unix" + snapshot_directory_visible = false + encryption_key_source = "Microsoft.KeyVault" + key_vault_private_endpoint_id = azurerm_private_endpoint.test.id + network_features = "Standard" + + 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.RandomInteger, tenantID, "eastus") +} + +func (NetAppVolumeGroupOracleResource) templatePpgOracle(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + resource_group { + prevent_deletion_if_contains_resources = false + } + netapp { + prevent_volume_destruction = 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 = { + "SkipNRMSNSG" = "true" + } +} + +resource "azurerm_user_assigned_identity" "test" { + name = "user-assigned-identity-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z" + } +} + +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.0.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.0.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.0.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 + + platform_update_domain_count = 2 + platform_fault_domain_count = 2 + + 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_D2s_v4" + 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 = "0001-com-ubuntu-server-jammy" + sku = "22_04-lts" + version = "latest" + } + + patch_assessment_mode = "AutomaticByPlatform" + + os_disk { + storage_account_type = "Standard_LRS" + caching = "ReadWrite" + } + + identity { + type = "SystemAssigned, UserAssigned" + identity_ids = [ + azurerm_user_assigned_identity.test.id + ] + } + + tags = { + "AzSecPackAutoConfigReady" = "true", + "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 = 4 + qos_type = "Manual" + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} +`, data.RandomInteger, data.Locations.Primary) +} + +func (NetAppVolumeGroupOracleResource) templateAvailabilityZoneOracle(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + resource_group { + prevent_deletion_if_contains_resources = false + } + netapp { + prevent_volume_destruction = false + } + } +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-netapp-%[1]d" + location = "%[2]s" + + tags = { + "SkipNRMSNSG" = "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.0.0.0/16"] +} + +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.0.2.0/24"] + + delegation { + name = "testdelegation" + + service_delegation { + name = "Microsoft.Netapp/volumes" + actions = ["Microsoft.Network/networkinterfaces/*", "Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +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 + ] +} + +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 = 4 + qos_type = "Manual" +} +`, data.RandomInteger, data.Locations.Primary) +} 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 index 7aedf41ca4712..2e271fcf6ee67 100644 --- a/internal/services/netapp/netapp_volume_group_sap_hana_data_source.go +++ b/internal/services/netapp/netapp_volume_group_sap_hana_data_source.go @@ -15,42 +15,45 @@ import ( "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2024-03-01/volumegroups" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" netAppModels "github.com/hashicorp/terraform-provider-azurerm/internal/services/netapp/models" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/netapp/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" ) -var _ sdk.DataSource = NetAppVolumeGroupSapHanaDataSource{} +var _ sdk.DataSource = NetAppVolumeGroupSAPHanaDataSource{} -type NetAppVolumeGroupSapHanaDataSource struct{} +type NetAppVolumeGroupSAPHanaDataSource struct{} -func (r NetAppVolumeGroupSapHanaDataSource) ResourceType() string { +func (r NetAppVolumeGroupSAPHanaDataSource) ResourceType() string { return "azurerm_netapp_volume_group_sap_hana" } -func (r NetAppVolumeGroupSapHanaDataSource) ModelObject() interface{} { - return &netAppModels.NetAppVolumeGroupSapHanaDataSourceModel{} +func (r NetAppVolumeGroupSAPHanaDataSource) ModelObject() interface{} { + return &netAppModels.NetAppVolumeGroupSAPHanaDataSourceModel{} } -func (r NetAppVolumeGroupSapHanaDataSource) IDValidationFunc() pluginsdk.SchemaValidateFunc { +func (r NetAppVolumeGroupSAPHanaDataSource) IDValidationFunc() pluginsdk.SchemaValidateFunc { return volumegroups.ValidateVolumeGroupID } -func (r NetAppVolumeGroupSapHanaDataSource) Arguments() map[string]*pluginsdk.Schema { +func (r NetAppVolumeGroupSAPHanaDataSource) Arguments() map[string]*pluginsdk.Schema { return map[string]*pluginsdk.Schema{ "name": { - Type: pluginsdk.TypeString, - Required: true, + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.VolumeGroupName, }, "resource_group_name": commonschema.ResourceGroupName(), "account_name": { - Type: pluginsdk.TypeString, - Required: true, + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.AccountName, }, } } -func (r NetAppVolumeGroupSapHanaDataSource) Attributes() map[string]*pluginsdk.Schema { +func (r NetAppVolumeGroupSAPHanaDataSource) Attributes() map[string]*pluginsdk.Schema { return map[string]*pluginsdk.Schema{ "location": commonschema.LocationComputed(), @@ -233,13 +236,13 @@ func (r NetAppVolumeGroupSapHanaDataSource) Attributes() map[string]*pluginsdk.S } } -func (r NetAppVolumeGroupSapHanaDataSource) Read() sdk.ResourceFunc { +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 netAppModels.NetAppVolumeGroupSapHanaDataSourceModel + var state netAppModels.NetAppVolumeGroupSAPHanaDataSourceModel if err := metadata.Decode(&state); err != nil { return fmt.Errorf("decoding: %+v", err) } @@ -262,7 +265,7 @@ func (r NetAppVolumeGroupSapHanaDataSource) Read() sdk.ResourceFunc { state.GroupDescription = pointer.From(groupMetaData.GroupDescription) } - volumes, err := flattenNetAppVolumeGroupVolumes(ctx, props.Volumes, metadata) + volumes, err := flattenNetAppVolumeGroupSAPHanaVolumes(ctx, props.Volumes, metadata) if err != nil { return fmt.Errorf("setting `volume`: %+v", err) } 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 index 84014a2ad8e7d..576ff656d34cc 100644 --- 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 @@ -11,11 +11,11 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" ) -type NetAppVolumeGroupSapHanaDataSource struct{} +type NetAppVolumeGroupSAPHanaDataSource struct{} -func TestAccNetAppVolumeGroupSapHanaDataSource_basic(t *testing.T) { +func TestAccNetAppVolumeGroupSAPHanaDataSource_basic(t *testing.T) { data := acceptance.BuildTestData(t, "data.azurerm_netapp_volume_group_sap_hana", "test") - d := NetAppVolumeGroupSapHanaDataSource{} + d := NetAppVolumeGroupSAPHanaDataSource{} data.DataSourceTest(t, []acceptance.TestStep{ { @@ -29,7 +29,7 @@ func TestAccNetAppVolumeGroupSapHanaDataSource_basic(t *testing.T) { }) } -func (d NetAppVolumeGroupSapHanaDataSource) basic(data acceptance.TestData) string { +func (d NetAppVolumeGroupSAPHanaDataSource) basic(data acceptance.TestData) string { return fmt.Sprintf(` %s @@ -38,5 +38,5 @@ data "azurerm_netapp_volume_group_sap_hana" "test" { 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)) +`, 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 index c8c5e76f18839..3259df5fd5638 100644 --- a/internal/services/netapp/netapp_volume_group_sap_hana_resource.go +++ b/internal/services/netapp/netapp_volume_group_sap_hana_resource.go @@ -28,23 +28,23 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/utils" ) -type NetAppVolumeGroupSapHanaResource struct{} +type NetAppVolumeGroupSAPHanaResource struct{} -var _ sdk.Resource = NetAppVolumeGroupSapHanaResource{} +var _ sdk.Resource = NetAppVolumeGroupSAPHanaResource{} -func (r NetAppVolumeGroupSapHanaResource) ModelObject() interface{} { - return &netAppModels.NetAppVolumeGroupSapHanaModel{} +func (r NetAppVolumeGroupSAPHanaResource) ModelObject() interface{} { + return &netAppModels.NetAppVolumeGroupSAPHanaModel{} } -func (r NetAppVolumeGroupSapHanaResource) ResourceType() string { +func (r NetAppVolumeGroupSAPHanaResource) ResourceType() string { return "azurerm_netapp_volume_group_sap_hana" } -func (r NetAppVolumeGroupSapHanaResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { +func (r NetAppVolumeGroupSAPHanaResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { return volumegroups.ValidateVolumeGroupID } -func (r NetAppVolumeGroupSapHanaResource) Arguments() map[string]*pluginsdk.Schema { +func (r NetAppVolumeGroupSAPHanaResource) Arguments() map[string]*pluginsdk.Schema { return map[string]*pluginsdk.Schema{ "name": { Type: pluginsdk.TypeString, @@ -115,7 +115,7 @@ func (r NetAppVolumeGroupSapHanaResource) Arguments() map[string]*pluginsdk.Sche Type: pluginsdk.TypeString, Required: true, ForceNew: true, - ValidateFunc: validation.StringInSlice(netAppValidate.PossibleValuesForVolumeSpecNameSapHana(), false), + ValidateFunc: validation.StringInSlice(netAppValidate.PossibleValuesForVolumeSpecNameSAPHana(), false), }, "volume_path": { @@ -151,7 +151,7 @@ func (r NetAppVolumeGroupSapHanaResource) Arguments() map[string]*pluginsdk.Sche MaxItems: 1, Elem: &pluginsdk.Schema{ Type: pluginsdk.TypeString, - ValidateFunc: validation.StringInSlice(netAppValidate.PossibleValuesForProtocolTypeVolumeGroupSapHana(), false), + ValidateFunc: validation.StringInSlice(netAppValidate.PossibleValuesForProtocolTypeVolumeGroupSAPHana(), false), }, }, @@ -274,6 +274,7 @@ func (r NetAppVolumeGroupSapHanaResource) Arguments() map[string]*pluginsdk.Sche "data_protection_snapshot_policy": { Type: pluginsdk.TypeList, Optional: true, + Computed: true, // O+C - Adding this because Terraform is not being able to build proper deletion graph, it is trying to delete the snapshot policy before the volume because this is in a deeper level within the schema inside an array of volumes MaxItems: 1, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ @@ -291,11 +292,11 @@ func (r NetAppVolumeGroupSapHanaResource) Arguments() map[string]*pluginsdk.Sche } } -func (r NetAppVolumeGroupSapHanaResource) Attributes() map[string]*pluginsdk.Schema { +func (r NetAppVolumeGroupSAPHanaResource) Attributes() map[string]*pluginsdk.Schema { return map[string]*pluginsdk.Schema{} } -func (r NetAppVolumeGroupSapHanaResource) Create() sdk.ResourceFunc { +func (r NetAppVolumeGroupSAPHanaResource) Create() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 90 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { @@ -304,7 +305,7 @@ func (r NetAppVolumeGroupSapHanaResource) Create() sdk.ResourceFunc { subscriptionId := metadata.Client.Account.SubscriptionId - var model netAppModels.NetAppVolumeGroupSapHanaModel + var model netAppModels.NetAppVolumeGroupSAPHanaModel if err := metadata.Decode(&model); err != nil { return fmt.Errorf("decoding: %+v", err) } @@ -321,7 +322,7 @@ func (r NetAppVolumeGroupSapHanaResource) Create() sdk.ResourceFunc { return metadata.ResourceRequiresImport(r.ResourceType(), id) } - volumeList, err := expandNetAppVolumeGroupVolumes(model.Volumes) + volumeList, err := expandNetAppVolumeGroupSAPHanaVolumes(model.Volumes) if err != nil { return err } @@ -354,14 +355,13 @@ func (r NetAppVolumeGroupSapHanaResource) Create() sdk.ResourceFunc { }, } - err = client.CreateThenPoll(ctx, id, parameters) - if err != nil { + if err = client.CreateThenPoll(ctx, id, parameters); 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 + return fmt.Errorf("waiting for create of %s: %+v", id, err) } // CRR - Authorizing secondaries from primary volumes @@ -411,7 +411,7 @@ func (r NetAppVolumeGroupSapHanaResource) Create() sdk.ResourceFunc { } } -func (r NetAppVolumeGroupSapHanaResource) Update() sdk.ResourceFunc { +func (r NetAppVolumeGroupSAPHanaResource) Update() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 120 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { @@ -423,7 +423,7 @@ func (r NetAppVolumeGroupSapHanaResource) Update() sdk.ResourceFunc { } metadata.Logger.Infof("Decoding state for %s", id) - var state netAppModels.NetAppVolumeGroupSapHanaModel + var state netAppModels.NetAppVolumeGroupSAPHanaModel if err := metadata.Decode(&state); err != nil { return fmt.Errorf("decoding: %+v", err) } @@ -473,7 +473,7 @@ func (r NetAppVolumeGroupSapHanaResource) Update() sdk.ResourceFunc { rule.Nfsv3 = utils.Bool(v["nfsv3_enabled"].(bool)) rule.Nfsv41 = utils.Bool(v["nfsv41_enabled"].(bool)) - errors = append(errors, netAppValidate.ValidateNetAppVolumeGroupExportPolicyRuleSAPHanna(rule, volumeProtocol)...) + errors = append(errors, netAppValidate.ValidateNetAppVolumeGroupExportPolicyRule(rule, volumeProtocol)...) } } @@ -515,6 +515,11 @@ func (r NetAppVolumeGroupSapHanaResource) Update() sdk.ResourceFunc { if err = volumeClient.UpdateThenPoll(ctx, volumeId, update); err != nil { return fmt.Errorf("updating %s: %+v", volumeId, err) } + + // Waiting for volume be completely updated + if err := waitForVolumeCreateOrUpdate(ctx, volumeClient, volumeId); err != nil { + return fmt.Errorf("waiting for update of %s: %+v", volumeId, err) + } } } } @@ -524,7 +529,7 @@ func (r NetAppVolumeGroupSapHanaResource) Update() sdk.ResourceFunc { } } -func (r NetAppVolumeGroupSapHanaResource) Read() sdk.ResourceFunc { +func (r NetAppVolumeGroupSAPHanaResource) Read() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 5 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { @@ -536,7 +541,7 @@ func (r NetAppVolumeGroupSapHanaResource) Read() sdk.ResourceFunc { } metadata.Logger.Infof("Decoding state for %s", id) - var state netAppModels.NetAppVolumeGroupSapHanaModel + var state netAppModels.NetAppVolumeGroupSAPHanaModel if err := metadata.Decode(&state); err != nil { return fmt.Errorf("decoding: %+v", err) } @@ -549,7 +554,7 @@ func (r NetAppVolumeGroupSapHanaResource) Read() sdk.ResourceFunc { return fmt.Errorf("retrieving %s: %v", id, err) } - model := netAppModels.NetAppVolumeGroupSapHanaModel{ + model := netAppModels.NetAppVolumeGroupSAPHanaModel{ Name: id.VolumeGroupName, AccountName: id.NetAppAccountName, Location: location.NormalizeNilable(existing.Model.Location), @@ -560,7 +565,7 @@ func (r NetAppVolumeGroupSapHanaResource) Read() sdk.ResourceFunc { model.GroupDescription = pointer.From(props.GroupMetaData.GroupDescription) model.ApplicationIdentifier = pointer.From(props.GroupMetaData.ApplicationIdentifier) - volumes, err := flattenNetAppVolumeGroupVolumes(ctx, props.Volumes, metadata) + volumes, err := flattenNetAppVolumeGroupSAPHanaVolumes(ctx, props.Volumes, metadata) if err != nil { return fmt.Errorf("setting `volume`: %+v", err) } @@ -575,7 +580,7 @@ func (r NetAppVolumeGroupSapHanaResource) Read() sdk.ResourceFunc { } } -func (r NetAppVolumeGroupSapHanaResource) Delete() sdk.ResourceFunc { +func (r NetAppVolumeGroupSAPHanaResource) Delete() sdk.ResourceFunc { return sdk.ResourceFunc{ Timeout: 120 * time.Minute, Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { @@ -605,7 +610,7 @@ func (r NetAppVolumeGroupSapHanaResource) Delete() sdk.ResourceFunc { } } - // Removing Volume Group + // Deleting Volume Group if err = client.DeleteThenPoll(ctx, pointer.From(id)); err != nil { return fmt.Errorf("deleting %s: %+v", pointer.From(id), err) } 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 index ed24ff62567be..fd2a374dad3d9 100644 --- a/internal/services/netapp/netapp_volume_group_sap_hana_resource_test.go +++ b/internal/services/netapp/netapp_volume_group_sap_hana_resource_test.go @@ -17,11 +17,11 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/utils" ) -type NetAppVolumeGroupSapHanaResource struct{} +type NetAppVolumeGroupSAPHanaResource struct{} -func TestAccNetAppVolumeGroupSapHana_basic(t *testing.T) { +func TestAccNetAppVolumeGroupSAPHana_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_sap_hana", "test") - r := NetAppVolumeGroupSapHanaResource{} + r := NetAppVolumeGroupSAPHanaResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { @@ -34,9 +34,9 @@ func TestAccNetAppVolumeGroupSapHana_basic(t *testing.T) { }) } -func TestAccNetAppVolumeGroupSapHana_backupVolumeSpecsNfsv3(t *testing.T) { +func TestAccNetAppVolumeGroupSAPHana_backupVolumeSpecsNfsv3(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_sap_hana", "test") - r := NetAppVolumeGroupSapHanaResource{} + r := NetAppVolumeGroupSAPHanaResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { @@ -49,9 +49,9 @@ func TestAccNetAppVolumeGroupSapHana_backupVolumeSpecsNfsv3(t *testing.T) { }) } -func TestAccNetAppVolumeGroupSapHana_snapshotPolicy(t *testing.T) { +func TestAccNetAppVolumeGroupSAPHana_snapshotPolicy(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_sap_hana", "test") - r := NetAppVolumeGroupSapHanaResource{} + r := NetAppVolumeGroupSAPHanaResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { @@ -64,9 +64,9 @@ func TestAccNetAppVolumeGroupSapHana_snapshotPolicy(t *testing.T) { }) } -func TestAccNetAppVolumeGroupSapHana_snapshotPolicyUpdate(t *testing.T) { +func TestAccNetAppVolumeGroupSAPHana_snapshotPolicyUpdate(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_sap_hana", "test") - r := NetAppVolumeGroupSapHanaResource{} + r := NetAppVolumeGroupSAPHanaResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { @@ -86,9 +86,9 @@ func TestAccNetAppVolumeGroupSapHana_snapshotPolicyUpdate(t *testing.T) { }) } -func TestAccNetAppVolumeGroupSapHana_volumeUpdates(t *testing.T) { +func TestAccNetAppVolumeGroupSAPHana_volumeUpdates(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_sap_hana", "test") - r := NetAppVolumeGroupSapHanaResource{} + r := NetAppVolumeGroupSAPHanaResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { @@ -104,19 +104,15 @@ func TestAccNetAppVolumeGroupSapHana_volumeUpdates(t *testing.T) { 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) { +func TestAccNetAppVolumeGroupSAPHana_crossRegionReplication(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_netapp_volume_group_sap_hana", "test_secondary") - r := NetAppVolumeGroupSapHanaResource{} + r := NetAppVolumeGroupSAPHanaResource{} data.ResourceTest(t, r, []acceptance.TestStep{ { @@ -129,7 +125,7 @@ func TestAccNetAppVolumeGroupSapHana_crossRegionReplication(t *testing.T) { }) } -func (t NetAppVolumeGroupSapHanaResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { +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 @@ -146,8 +142,8 @@ func (t NetAppVolumeGroupSapHanaResource) Exists(ctx context.Context, clients *c return utils.Bool(true), nil } -func (NetAppVolumeGroupSapHanaResource) basic(data acceptance.TestData) string { - template := NetAppVolumeGroupSapHanaResource{}.templatePPG(data) +func (NetAppVolumeGroupSAPHanaResource) basic(data acceptance.TestData) string { + template := NetAppVolumeGroupSAPHanaResource{}.templatePPG(data) return fmt.Sprintf(` %[1]s @@ -249,64 +245,6 @@ resource "azurerm_netapp_volume_group_sap_hana" "test" { } } - 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 @@ -315,8 +253,8 @@ resource "azurerm_netapp_volume_group_sap_hana" "test" { `, template, data.RandomInteger) } -func (NetAppVolumeGroupSapHanaResource) backupVolumeSpecsNfsv3(data acceptance.TestData) string { - template := NetAppVolumeGroupSapHanaResource{}.templatePPG(data) +func (NetAppVolumeGroupSAPHanaResource) backupVolumeSpecsNfsv3(data acceptance.TestData) string { + template := NetAppVolumeGroupSAPHanaResource{}.templatePPG(data) return fmt.Sprintf(` %[1]s @@ -388,36 +326,6 @@ resource "azurerm_netapp_volume_group_sap_hana" "test" { } } - 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" @@ -476,6 +384,36 @@ resource "azurerm_netapp_volume_group_sap_hana" "test" { } } + volume { + name = "acctest-NetAppVolume-6-%[2]d" + volume_path = "my-unique-file-path-6-%[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" + } + } + depends_on = [ azurerm_linux_virtual_machine.test, azurerm_proximity_placement_group.test @@ -484,8 +422,8 @@ resource "azurerm_netapp_volume_group_sap_hana" "test" { `, template, data.RandomInteger) } -func (NetAppVolumeGroupSapHanaResource) avgSnapshotPolicy(data acceptance.TestData) string { - template := NetAppVolumeGroupSapHanaResource{}.templatePPG(data) +func (NetAppVolumeGroupSAPHanaResource) avgSnapshotPolicy(data acceptance.TestData) string { + template := NetAppVolumeGroupSAPHanaResource{}.templatePPG(data) return fmt.Sprintf(` %[1]s @@ -609,76 +547,6 @@ resource "azurerm_netapp_volume_group_sap_hana" "test" { 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" @@ -687,14 +555,15 @@ resource "azurerm_netapp_volume_group_sap_hana" "test" { depends_on = [ azurerm_linux_virtual_machine.test, - azurerm_proximity_placement_group.test + azurerm_proximity_placement_group.test, + azurerm_netapp_snapshot_policy.test ] } `, template, data.RandomInteger) } -func (NetAppVolumeGroupSapHanaResource) updateAvgSnapshotPolicy(data acceptance.TestData) string { - template := NetAppVolumeGroupSapHanaResource{}.templatePPG(data) +func (NetAppVolumeGroupSAPHanaResource) updateAvgSnapshotPolicy(data acceptance.TestData) string { + template := NetAppVolumeGroupSAPHanaResource{}.templatePPG(data) return fmt.Sprintf(` %[1]s @@ -750,10 +619,6 @@ resource "azurerm_netapp_volume_group_sap_hana" "test" { 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" @@ -784,10 +649,6 @@ resource "azurerm_netapp_volume_group_sap_hana" "test" { 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" @@ -818,76 +679,6 @@ resource "azurerm_netapp_volume_group_sap_hana" "test" { 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" @@ -896,14 +687,15 @@ resource "azurerm_netapp_volume_group_sap_hana" "test" { depends_on = [ azurerm_linux_virtual_machine.test, - azurerm_proximity_placement_group.test + azurerm_proximity_placement_group.test, + azurerm_netapp_snapshot_policy.test ] } `, template, data.RandomInteger) } -func (NetAppVolumeGroupSapHanaResource) updateVolumes(data acceptance.TestData) string { - template := NetAppVolumeGroupSapHanaResource{}.templatePPG(data) +func (NetAppVolumeGroupSAPHanaResource) updateVolumes(data acceptance.TestData) string { + template := NetAppVolumeGroupSAPHanaResource{}.templatePPG(data) return fmt.Sprintf(` %[1]s @@ -999,70 +791,12 @@ resource "azurerm_netapp_volume_group_sap_hana" "test" { 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 @@ -1071,8 +805,8 @@ resource "azurerm_netapp_volume_group_sap_hana" "test" { `, template, data.RandomInteger) } -func (NetAppVolumeGroupSapHanaResource) crossRegionReplication(data acceptance.TestData) string { - template := NetAppVolumeGroupSapHanaResource{}.templateForAvgCrossRegionReplication(data) +func (NetAppVolumeGroupSAPHanaResource) crossRegionReplication(data acceptance.TestData) string { + template := NetAppVolumeGroupSAPHanaResource{}.templateForAvgCrossRegionReplication(data) return fmt.Sprintf(` %[1]s @@ -1174,64 +908,6 @@ resource "azurerm_netapp_volume_group_sap_hana" "test_primary" { } } - 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 @@ -1350,94 +1026,27 @@ resource "azurerm_netapp_volume_group_sap_hana" "test_secondary" { } } - 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, "westus") +`, template, data.RandomInteger, "westus2") } -func (r NetAppVolumeGroupSapHanaResource) templateForAvgCrossRegionReplication(data acceptance.TestData) string { - template := NetAppVolumeGroupSapHanaResource{}.templatePPG(data) +func (r NetAppVolumeGroupSAPHanaResource) templateForAvgCrossRegionReplication(data acceptance.TestData) string { + template := NetAppVolumeGroupSAPHanaResource{}.templatePPG(data) return fmt.Sprintf(` %[1]s +resource "azurerm_user_assigned_identity" "test_secondary" { + name = "user-assigned-identity-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + resource "azurerm_network_security_group" "test_secondary" { name = "acctest-NSG-Secondary-%[2]d" location = "%[3]s" @@ -1453,7 +1062,7 @@ 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"] + address_space = ["10.88.0.0/16"] tags = { "CreatedOnDate" = "2022-07-08T23:50:21Z", @@ -1465,7 +1074,7 @@ 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"] + address_prefixes = ["10.88.2.0/24"] delegation { name = "testdelegation" @@ -1481,7 +1090,7 @@ 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"] + address_prefixes = ["10.88.1.0/24"] } resource "azurerm_subnet_network_security_group_association" "test_secondary" { @@ -1505,6 +1114,9 @@ resource "azurerm_availability_set" "test_secondary" { location = "%[3]s" resource_group_name = azurerm_resource_group.test.name + platform_update_domain_count = 2 + platform_fault_domain_count = 2 + proximity_placement_group_id = azurerm_proximity_placement_group.test_secondary.id tags = { @@ -1534,7 +1146,7 @@ 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" + size = "Standard_D2s_v4" admin_username = local.admin_username admin_password = local.admin_password disable_password_authentication = false @@ -1551,12 +1163,22 @@ resource "azurerm_linux_virtual_machine" "test_secondary" { version = "latest" } + patch_assessment_mode = "AutomaticByPlatform" + os_disk { storage_account_type = "Standard_LRS" caching = "ReadWrite" } + identity { + type = "SystemAssigned, UserAssigned" + identity_ids = [ + azurerm_user_assigned_identity.test_secondary.id + ] + } + tags = { + "AzSecPackAutoConfigReady" = "true", "platformsettings.host_environment.service.platform_optedin_for_rootcerts" = "true", "CreatedOnDate" = "2022-07-08T23:50:21Z", "SkipASMAzSecPack" = "true", @@ -1594,13 +1216,12 @@ resource "azurerm_netapp_pool" "test_secondary" { "SkipASMAzSecPack" = "true" } } -`, template, data.RandomInteger, "westus") +`, template, data.RandomInteger, "westus2") } -func (NetAppVolumeGroupSapHanaResource) templatePPG(data acceptance.TestData) string { +func (NetAppVolumeGroupSAPHanaResource) templatePPG(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { - alias = "all2" features { resource_group { prevent_deletion_if_contains_resources = false @@ -1628,6 +1249,12 @@ resource "azurerm_resource_group" "test" { } } +resource "azurerm_user_assigned_identity" "test" { + name = "user-assigned-identity-%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name +} + resource "azurerm_network_security_group" "test" { name = "acctest-NSG-%[1]d" location = azurerm_resource_group.test.location @@ -1643,7 +1270,7 @@ 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"] + address_space = ["10.88.0.0/16"] tags = { "CreatedOnDate" = "2022-07-08T23:50:21Z", @@ -1655,7 +1282,7 @@ 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"] + address_prefixes = ["10.88.2.0/24"] delegation { name = "testdelegation" @@ -1671,7 +1298,7 @@ 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"] + address_prefixes = ["10.88.1.0/24"] } resource "azurerm_subnet_network_security_group_association" "public" { @@ -1695,6 +1322,9 @@ resource "azurerm_availability_set" "test" { location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name + platform_update_domain_count = 2 + platform_fault_domain_count = 2 + proximity_placement_group_id = azurerm_proximity_placement_group.test.id tags = { @@ -1724,7 +1354,7 @@ 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" + size = "Standard_D2s_v4" admin_username = local.admin_username admin_password = local.admin_password disable_password_authentication = false @@ -1741,12 +1371,22 @@ resource "azurerm_linux_virtual_machine" "test" { version = "latest" } + patch_assessment_mode = "AutomaticByPlatform" + os_disk { storage_account_type = "Standard_LRS" caching = "ReadWrite" } + identity { + type = "SystemAssigned, UserAssigned" + identity_ids = [ + azurerm_user_assigned_identity.test.id + ] + } + tags = { + "AzSecPackAutoConfigReady" = "true", "platformsettings.host_environment.service.platform_optedin_for_rootcerts" = "true", "CreatedOnDate" = "2022-07-08T23:50:21Z", "SkipASMAzSecPack" = "true", @@ -1784,5 +1424,5 @@ resource "azurerm_netapp_pool" "test" { "SkipASMAzSecPack" = "true" } } -`, data.RandomInteger, "eastus") +`, data.RandomInteger, "westus3") } diff --git a/internal/services/netapp/netapp_volume_helper.go b/internal/services/netapp/netapp_volume_helper.go index 67c93b7cfe42f..6b4f8642f77b0 100644 --- a/internal/services/netapp/netapp_volume_helper.go +++ b/internal/services/netapp/netapp_volume_helper.go @@ -6,6 +6,7 @@ package netapp import ( "context" "fmt" + "log" "strconv" "strings" "time" @@ -100,9 +101,9 @@ func expandNetAppVolumeGroupDataProtectionSnapshotPolicy(input []netAppModels.Da } } -func expandNetAppVolumeGroupVolumes(input []netAppModels.NetAppVolumeGroupVolume) (*[]volumegroups.VolumeGroupVolumeProperties, error) { +func expandNetAppVolumeGroupSAPHanaVolumes(input []netAppModels.NetAppVolumeGroupSAPHanaVolume) (*[]volumegroups.VolumeGroupVolumeProperties, error) { if len(input) == 0 { - return &[]volumegroups.VolumeGroupVolumeProperties{}, fmt.Errorf("received empty NetAppVolumeGroupVolume slice") + return &[]volumegroups.VolumeGroupVolumeProperties{}, fmt.Errorf("received empty NetAppVolumeGroupSAPHanaVolume slice") } results := make([]volumegroups.VolumeGroupVolumeProperties, 0) @@ -153,6 +154,60 @@ func expandNetAppVolumeGroupVolumes(input []netAppModels.NetAppVolumeGroupVolume return &results, nil } +func expandNetAppVolumeGroupOracleVolumes(input []netAppModels.NetAppVolumeGroupOracleVolume) (*[]volumegroups.VolumeGroupVolumeProperties, error) { + if len(input) == 0 { + return &[]volumegroups.VolumeGroupVolumeProperties{}, fmt.Errorf("received empty NetAppVolumeGroupSAPHanaVolume slice") + } + + results := make([]volumegroups.VolumeGroupVolumeProperties, 0) + + for _, item := range input { + storageQuotaInGB := item.StorageQuotaInGB * 1073741824 + + volumeProperties := &volumegroups.VolumeGroupVolumeProperties{ + Name: pointer.To(item.Name), + Properties: volumegroups.VolumeProperties{ + CapacityPoolResourceId: pointer.To(item.CapacityPoolId), + CreationToken: item.VolumePath, + ServiceLevel: pointer.To(volumegroups.ServiceLevel(item.ServiceLevel)), + SubnetId: item.SubnetId, + ProtocolTypes: pointer.To(item.Protocols), + SecurityStyle: pointer.To(volumegroups.SecurityStyle(item.SecurityStyle)), + UsageThreshold: storageQuotaInGB, + ExportPolicy: expandNetAppVolumeGroupVolumeExportPolicyRule(item.ExportPolicy), + SnapshotDirectoryVisible: pointer.To(item.SnapshotDirectoryVisible), + ThroughputMibps: utils.Float(item.ThroughputInMibps), + VolumeSpecName: utils.String(item.VolumeSpecName), + NetworkFeatures: pointer.To(volumegroups.NetworkFeatures(item.NetworkFeatures)), + DataProtection: &volumegroups.VolumePropertiesDataProtection{ + Snapshot: expandNetAppVolumeGroupDataProtectionSnapshotPolicy(item.DataProtectionSnapshotPolicy).Snapshot, + }, + }, + Tags: &item.Tags, + } + + if v := item.ProximityPlacementGroupId; v != "" { + volumeProperties.Properties.ProximityPlacementGroup = pointer.To(v) + } + + if v := item.Zone; v != "" { + volumeProperties.Zones = pointer.To([]string{v}) + } + + if v := item.EncryptionKeySource; v != "" { + volumeProperties.Properties.EncryptionKeySource = pointer.To(volumegroups.EncryptionKeySource(v)) + } + + if v := item.KeyVaultPrivateEndpointId; v != "" { + volumeProperties.Properties.KeyVaultPrivateEndpointResourceId = pointer.To(v) + } + + results = append(results, *volumeProperties) + } + + return &results, nil +} + func expandNetAppVolumeGroupVolumeExportPolicyRulePatch(input []interface{}) *volumes.VolumePatchPropertiesExportPolicy { if len(input) == 0 { return &volumes.VolumePatchPropertiesExportPolicy{} @@ -257,7 +312,11 @@ func expandNetAppVolumeDataProtectionSnapshotPolicy(input []interface{}) *volume func expandNetAppVolumeDataProtectionSnapshotPolicyPatch(input []interface{}) *volumes.VolumePatchPropertiesDataProtection { if len(input) == 0 { - return &volumes.VolumePatchPropertiesDataProtection{} + return &volumes.VolumePatchPropertiesDataProtection{ + Snapshot: &volumes.VolumeSnapshotProperties{ + SnapshotPolicyId: pointer.To(""), + }, + } } snapshotObject := volumes.VolumeSnapshotProperties{} @@ -325,15 +384,15 @@ func expandNetAppVolumeDataProtectionBackupPolicyPatch(input []interface{}) *vol } } -func flattenNetAppVolumeGroupVolumes(ctx context.Context, input *[]volumegroups.VolumeGroupVolumeProperties, metadata sdk.ResourceMetaData) ([]netAppModels.NetAppVolumeGroupVolume, error) { - results := make([]netAppModels.NetAppVolumeGroupVolume, 0) +func flattenNetAppVolumeGroupSAPHanaVolumes(ctx context.Context, input *[]volumegroups.VolumeGroupVolumeProperties, metadata sdk.ResourceMetaData) ([]netAppModels.NetAppVolumeGroupSAPHanaVolume, error) { + results := make([]netAppModels.NetAppVolumeGroupSAPHanaVolume, 0) if input == nil || len(pointer.From(input)) == 0 { return results, fmt.Errorf("received empty volumegroups.VolumeGroupVolumeProperties slice") } for _, item := range *input { - volumeGroupVolume := netAppModels.NetAppVolumeGroupVolume{} + volumeGroupVolume := netAppModels.NetAppVolumeGroupSAPHanaVolume{} props := item.Properties volumeGroupVolume.Name = getUserDefinedVolumeName(item.Name) @@ -346,7 +405,11 @@ func flattenNetAppVolumeGroupVolumes(ctx context.Context, input *[]volumegroups. volumeGroupVolume.SnapshotDirectoryVisible = pointer.From(props.SnapshotDirectoryVisible) volumeGroupVolume.ThroughputInMibps = pointer.From(props.ThroughputMibps) volumeGroupVolume.Tags = pointer.From(item.Tags) - volumeGroupVolume.ProximityPlacementGroupId = pointer.From(props.ProximityPlacementGroup) + + if props.ProximityPlacementGroup != nil { + volumeGroupVolume.ProximityPlacementGroupId = pointer.From(props.ProximityPlacementGroup) + } + volumeGroupVolume.VolumeSpecName = pointer.From(props.VolumeSpecName) if props.UsageThreshold > 0 { @@ -367,12 +430,12 @@ func flattenNetAppVolumeGroupVolumes(ctx context.Context, input *[]volumegroups. volumeClient := metadata.Client.NetApp.VolumeClient id, err := volumes.ParseVolumeID(pointer.From(item.Id)) if err != nil { - return []netAppModels.NetAppVolumeGroupVolume{}, err + return []netAppModels.NetAppVolumeGroupSAPHanaVolume{}, err } standaloneVol, err := volumeClient.Get(ctx, pointer.From(id)) if err != nil { - return []netAppModels.NetAppVolumeGroupVolume{}, fmt.Errorf("retrieving %s: %v", id, err) + return []netAppModels.NetAppVolumeGroupSAPHanaVolume{}, fmt.Errorf("retrieving %s: %v", id, err) } if standaloneVol.Model.Properties.DataProtection != nil && standaloneVol.Model.Properties.DataProtection.Replication != nil { @@ -391,6 +454,85 @@ func flattenNetAppVolumeGroupVolumes(ctx context.Context, input *[]volumegroups. return results, nil } +func flattenNetAppVolumeGroupOracleVolumes(ctx context.Context, input *[]volumegroups.VolumeGroupVolumeProperties, metadata sdk.ResourceMetaData) ([]netAppModels.NetAppVolumeGroupOracleVolume, error) { + results := make([]netAppModels.NetAppVolumeGroupOracleVolume, 0) + + if input == nil || len(pointer.From(input)) == 0 { + return results, fmt.Errorf("received empty volumegroups.VolumeGroupVolumeProperties slice") + } + + for _, item := range *input { + volumeGroupVolume := netAppModels.NetAppVolumeGroupOracleVolume{} + + 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 = pointer.From(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.NetworkFeatures = string(pointer.From(props.NetworkFeatures)) + + if props.ProximityPlacementGroup != nil { + volumeGroupVolume.ProximityPlacementGroupId = pointer.From(props.ProximityPlacementGroup) + } + + if item.Zones != nil && len(pointer.From(item.Zones)) > 0 { + volumeGroupVolume.Zone = (pointer.From(item.Zones))[0] + } + + if props.EncryptionKeySource != nil { + volumeGroupVolume.EncryptionKeySource = pointer.From((*string)(props.EncryptionKeySource)) + } + + if props.KeyVaultPrivateEndpointResourceId != nil { + volumeGroupVolume.KeyVaultPrivateEndpointId = pointer.From(props.KeyVaultPrivateEndpointResourceId) + } + + 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 []netAppModels.NetAppVolumeGroupOracleVolume{}, err + } + + standaloneVol, err := volumeClient.Get(ctx, pointer.From(id)) + if err != nil { + return []netAppModels.NetAppVolumeGroupOracleVolume{}, fmt.Errorf("retrieving %s: %v", id, err) + } + + 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) []netAppModels.ExportPolicyRule { results := make([]netAppModels.ExportPolicyRule, 0) @@ -536,7 +678,9 @@ func deleteVolume(ctx context.Context, metadata sdk.ResourceMetaData, volumeId s } // Deleting replication and waiting for it to fully complete the operation - if err = replicationClient.VolumesDeleteReplicationThenPoll(ctx, pointer.From(replicaVolumeId)); err != nil { + // Can't use VolumesDeleteReplicationThenPoll because from time to time the LRO SDK fails, + // please see Pandora's issue: https://github.com/hashicorp/pandora/issues/4571 + if _, err = replicationClient.VolumesDeleteReplication(ctx, pointer.From(replicaVolumeId)); err != nil { return fmt.Errorf("deleting replicate %s: %+v", pointer.From(replicaVolumeId), err) } @@ -545,7 +689,30 @@ func deleteVolume(ctx context.Context, metadata sdk.ResourceMetaData, volumeId s } } + // Disassociating volume from snapshot policy if present + if existing.Model.Properties.DataProtection != nil && existing.Model.Properties.DataProtection.Snapshot != nil && existing.Model.Properties.DataProtection.Snapshot.SnapshotPolicyId != nil && existing.Model.Properties.DataProtection.Snapshot.SnapshotPolicyId != pointer.To("") { + log.Printf("[INFO] Disassociating volume from snapshot policy") + if err = client.UpdateThenPoll(ctx, pointer.From(id), volumes.VolumePatch{ + Properties: &volumes.VolumePatchProperties{ + DataProtection: &volumes.VolumePatchPropertiesDataProtection{ + Snapshot: &volumes.VolumeSnapshotProperties{ + SnapshotPolicyId: pointer.To(""), + }, + }, + }, + }); err != nil { + return fmt.Errorf("dissociating snapshot policy from %s: %+v", pointer.From(id), err) + } + + // Wait for the volume update to complete + log.Printf("[INFO] Wait for the volume update to complete after unsetting snapshot policy") + if err := waitForVolumeCreateOrUpdate(ctx, client, pointer.From(id)); err != nil { + return fmt.Errorf("waiting for volume to reflect snapshotPolicyId unset from %q: %+v", pointer.From(id), err) + } + } + // Deleting volume and waiting for it to fully complete the operation + log.Printf("[INFO] Deleting volume %s", id.String()) if err = client.DeleteThenPoll(ctx, pointer.From(id), volumes.DeleteOperationOptions{ ForceDelete: utils.Bool(true), }); err != nil { @@ -553,7 +720,7 @@ func deleteVolume(ctx context.Context, metadata sdk.ResourceMetaData, volumeId s } if err = waitForVolumeDeletion(ctx, client, pointer.From(id)); err != nil { - return fmt.Errorf("waiting for deletion of %s: %+v", pointer.From(id), err) + return fmt.Errorf("waiting delete %s: %+v", pointer.From(id), err) } return nil @@ -603,6 +770,28 @@ func waitForVolumeGroupCreateOrUpdate(ctx context.Context, client *volumegroups. return nil } +func waitForVolumeGroupDelete(ctx context.Context, client *volumegroups.VolumeGroupsClient, id volumegroups.VolumeGroupId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("internal-error: 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: netappVolumeGroupStateRefreshFunc(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 waitForReplAuthorization(ctx context.Context, client *volumesreplication.VolumesReplicationClient, id volumesreplication.VolumeId) error { deadline, ok := ctx.Deadline() if !ok { diff --git a/internal/services/netapp/netapp_volume_quota_rule_resource.go b/internal/services/netapp/netapp_volume_quota_rule_resource.go index 5dc39c9e63afe..5cbeb3017a336 100644 --- a/internal/services/netapp/netapp_volume_quota_rule_resource.go +++ b/internal/services/netapp/netapp_volume_quota_rule_resource.go @@ -128,11 +128,15 @@ func (r NetAppVolumeQuotaRuleResource) Create() sdk.ResourceFunc { }, } - err = client.CreateThenPoll(ctx, id, parameters) - if err != nil { + if err = client.CreateThenPoll(ctx, id, parameters); err != nil { return fmt.Errorf("creating %s: %+v", id, err) } + // Waiting for quota rule to be created + if err := waitForQuotaRuleCreateOrUpdate(ctx, client, id); err != nil { + return fmt.Errorf("waiting create %s: %+v", id, err) + } + metadata.SetID(id) return nil @@ -169,6 +173,11 @@ func (r NetAppVolumeQuotaRuleResource) Update() sdk.ResourceFunc { if err := client.UpdateThenPoll(ctx, pointer.From(id), update); err != nil { return fmt.Errorf("updating %s: %+v", id, err) } + + // Waiting for quota rule to be updated + if err := waitForQuotaRuleCreateOrUpdate(ctx, client, pointer.From(id)); err != nil { + return fmt.Errorf("waiting update %s: %+v", id, err) + } } return nil @@ -242,7 +251,73 @@ func (r NetAppVolumeQuotaRuleResource) Delete() sdk.ResourceFunc { return fmt.Errorf("deleting %s: %+v", pointer.From(id), err) } + // Waiting for quota rule to be deleted + if err := waitForQuotaRuleDelete(ctx, client, pointer.From(id)); err != nil { + return fmt.Errorf("waiting delete %s: %+v", id, err) + } + return nil }, } } + +func waitForQuotaRuleCreateOrUpdate(ctx context.Context, client *volumequotarules.VolumeQuotaRulesClient, id volumequotarules.VolumeQuotaRuleId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("internal-error: 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: netAppVolumeQuotaRuleStateRefreshFunc(ctx, client, id), + Timeout: time.Until(deadline), + } + + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for %s to finish creating/updating: %+v", id, err) + } + + return nil +} + +func netAppVolumeQuotaRuleStateRefreshFunc(ctx context.Context, client *volumequotarules.VolumeQuotaRulesClient, id volumequotarules.VolumeQuotaRuleId) 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 = fmt.Sprintf("%d", res.HttpResponse.StatusCode) + } + return res, statusCode, nil + } +} + +func waitForQuotaRuleDelete(ctx context.Context, client *volumequotarules.VolumeQuotaRulesClient, id volumequotarules.VolumeQuotaRuleId) error { + deadline, ok := ctx.Deadline() + if !ok { + return fmt.Errorf("internal-error: 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: netAppVolumeQuotaRuleStateRefreshFunc(ctx, client, id), + Timeout: time.Until(deadline), + } + + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for %s to finish deleting: %+v", id, err) + } + + return nil +} diff --git a/internal/services/netapp/netapp_volume_quota_rule_resource_test.go b/internal/services/netapp/netapp_volume_quota_rule_resource_test.go index 06f9ad3405b98..0af5ac6561119 100644 --- a/internal/services/netapp/netapp_volume_quota_rule_resource_test.go +++ b/internal/services/netapp/netapp_volume_quota_rule_resource_test.go @@ -198,7 +198,7 @@ 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"] + address_space = ["10.88.0.0/16"] tags = { "CreatedOnDate" = "2023-08-17T08:01:00Z", @@ -210,7 +210,7 @@ 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"] + address_prefixes = ["10.88.2.0/24"] delegation { name = "testdelegation" diff --git a/internal/services/netapp/netapp_volume_resource.go b/internal/services/netapp/netapp_volume_resource.go index a373860c7d328..105d417c97af5 100644 --- a/internal/services/netapp/netapp_volume_resource.go +++ b/internal/services/netapp/netapp_volume_resource.go @@ -894,7 +894,9 @@ func resourceNetAppVolumeDelete(d *pluginsdk.ResourceData, meta interface{}) err } // Breaking replication - if err = replicationClient.VolumesBreakReplicationThenPoll(ctx, *replicaVolumeId, volumesreplication.BreakReplicationRequest{ + // Can't use VolumesBreakReplicationThenPoll because from time to time the LRO SDK fails, + // please see Pandora's issue: https://github.com/hashicorp/pandora/issues/4571 + if _, err = replicationClient.VolumesBreakReplication(ctx, *replicaVolumeId, volumesreplication.BreakReplicationRequest{ ForceBreakReplication: utils.Bool(true), }); err != nil { return fmt.Errorf("breaking replication for %s: %+v", *replicaVolumeId, err) diff --git a/internal/services/netapp/netapp_volume_resource_test.go b/internal/services/netapp/netapp_volume_resource_test.go index 01b05f92dd38a..58008022d12db 100644 --- a/internal/services/netapp/netapp_volume_resource_test.go +++ b/internal/services/netapp/netapp_volume_resource_test.go @@ -136,6 +136,28 @@ func TestAccNetAppVolume_snapshotPolicy(t *testing.T) { }) } +func TestAccNetAppVolume_snapshotPolicyUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_netapp_volume", "test") + r := NetAppVolumeResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.snapshotPolicy(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.snapshotPolicyUpdate(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccNetAppVolume_crossRegionReplication(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_netapp_volume", "test_secondary") r := NetAppVolumeResource{} @@ -271,40 +293,6 @@ func TestAccNetAppVolume_update(t *testing.T) { }) } -func TestAccNetAppVolume_updateSubnet(t *testing.T) { - data := acceptance.BuildTestData(t, "azurerm_netapp_volume", "test") - r := NetAppVolumeResource{} - resourceGroupName := fmt.Sprintf("acctestRG-netapp-%d", data.RandomInteger) - oldVNetName := fmt.Sprintf("acctest-VirtualNetwork-%d", data.RandomInteger) - oldSubnetName := fmt.Sprintf("acctest-Subnet-%d", data.RandomInteger) - newVNetName := fmt.Sprintf("acctest-updated-VirtualNetwork-%d", data.RandomInteger) - newSubnetName := fmt.Sprintf("acctest-updated-Subnet-%d", data.RandomInteger) - uriTemplate := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Network/virtualNetworks/%s/subnets/%s" - - subscriptionID := os.Getenv("ARM_SUBSCRIPTION_ID") - oldSubnetId := fmt.Sprintf(uriTemplate, subscriptionID, resourceGroupName, oldVNetName, oldSubnetName) - newSubnetId := fmt.Sprintf(uriTemplate, subscriptionID, resourceGroupName, newVNetName, newSubnetName) - - data.ResourceTest(t, r, []acceptance.TestStep{ - { - Config: r.basic(data), - Check: acceptance.ComposeTestCheckFunc( - check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("subnet_id").HasValue(oldSubnetId), - ), - }, - data.ImportStep(), - { - Config: r.updateSubnet(data), - Check: acceptance.ComposeTestCheckFunc( - check.That(data.ResourceName).ExistsInAzure(r), - check.That(data.ResourceName).Key("subnet_id").HasValue(newSubnetId), - ), - }, - data.ImportStep(), - }) -} - func TestAccNetAppVolume_updateExportPolicyRule(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_netapp_volume", "test") r := NetAppVolumeResource{} @@ -774,6 +762,48 @@ resource "azurerm_netapp_volume" "test" { `, template, data.RandomInteger) } +func (NetAppVolumeResource) snapshotPolicyUpdate(data acceptance.TestData) string { + template := NetAppVolumeResource{}.template(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 + } +} + +resource "azurerm_netapp_volume" "test" { + name = "acctest-NetAppVolume-WithSnapshotPolicy-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + account_name = azurerm_netapp_account.test.name + pool_name = azurerm_netapp_pool.test.name + volume_path = "my-unique-file-path-%[2]d" + service_level = "Standard" + subnet_id = azurerm_subnet.test.id + protocols = ["NFSv3"] + security_style = "unix" + storage_quota_in_gb = 100 + throughput_in_mibps = 1.562 + + tags = { + "CreatedOnDate" = "2022-07-08T23:50:21Z", + "SkipASMAzSecPack" = "true" + } +} +`, template, data.RandomInteger) +} + func (NetAppVolumeResource) crossRegionReplication(data acceptance.TestData) string { template := NetAppVolumeResource{}.templateForCrossRegionReplication(data) return fmt.Sprintf(` @@ -1146,59 +1176,6 @@ resource "azurerm_netapp_volume" "test" { `, r.templatePoolQosManual(data), data.RandomInteger, data.RandomInteger) } -func (r NetAppVolumeResource) updateSubnet(data acceptance.TestData) string { - return fmt.Sprintf(` -%s - -resource "azurerm_virtual_network" "updated" { - name = "acctest-updated-VirtualNetwork-%d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - address_space = ["10.1.0.0/16"] - - tags = { - "CreatedOnDate" = "2022-07-08T23:50:21Z", - "SkipASMAzSecPack" = "true" - } -} - -resource "azurerm_subnet" "updated" { - name = "acctest-updated-Subnet-%d" - resource_group_name = azurerm_resource_group.test.name - virtual_network_name = azurerm_virtual_network.updated.name - address_prefixes = ["10.1.3.0/24"] - - delegation { - name = "testdelegation2" - - service_delegation { - name = "Microsoft.Netapp/volumes" - actions = ["Microsoft.Network/networkinterfaces/*", "Microsoft.Network/virtualNetworks/subnets/join/action"] - } - } -} - -resource "azurerm_netapp_volume" "test" { - name = "acctest-updated-NetAppVolume-%d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - account_name = azurerm_netapp_account.test.name - pool_name = azurerm_netapp_pool.test.name - volume_path = "my-updated-unique-file-path-%d" - service_level = "Standard" - subnet_id = azurerm_subnet.updated.id - protocols = ["NFSv3"] - storage_quota_in_gb = 100 - throughput_in_mibps = 1.562 - - tags = { - "CreatedOnDate" = "2022-07-08T23:50:21Z", - "SkipASMAzSecPack" = "true" - } -} -`, r.template(data), data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger) -} - func (r NetAppVolumeResource) updateExportPolicyRule(data acceptance.TestData) string { return fmt.Sprintf(` %s @@ -1241,7 +1218,7 @@ 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"] + address_space = ["10.88.0.0/16"] tags = { "CreatedOnDate" = "2022-07-08T23:50:21Z", @@ -1253,7 +1230,7 @@ resource "azurerm_subnet" "test_secondary" { name = "acctest-Subnet-secondary-%[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"] + address_prefixes = ["10.88.2.0/24"] delegation { name = "testdelegation" @@ -1312,7 +1289,7 @@ resource "azurerm_virtual_network" "test" { name = "acctest-VirtualNetwork-%d" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name - address_space = ["10.6.0.0/16"] + address_space = ["10.88.0.0/16"] tags = { "CreatedOnDate" = "2022-07-08T23:50:21Z", @@ -1324,7 +1301,7 @@ resource "azurerm_subnet" "test" { name = "acctest-Subnet-%d" resource_group_name = azurerm_resource_group.test.name virtual_network_name = azurerm_virtual_network.test.name - address_prefixes = ["10.6.2.0/24"] + address_prefixes = ["10.88.2.0/24"] delegation { name = "testdelegation" @@ -1395,7 +1372,7 @@ resource "azurerm_virtual_network" "test" { name = "acctest-VirtualNetwork-%d" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name - address_space = ["10.6.0.0/16"] + address_space = ["10.88.0.0/16"] tags = { "CreatedOnDate" = "2022-07-08T23:50:21Z", @@ -1407,7 +1384,7 @@ resource "azurerm_subnet" "test" { name = "acctest-Subnet-%d" resource_group_name = azurerm_resource_group.test.name virtual_network_name = azurerm_virtual_network.test.name - address_prefixes = ["10.6.2.0/24"] + address_prefixes = ["10.88.2.0/24"] delegation { name = "testdelegation" @@ -1449,13 +1426,11 @@ resource "azurerm_netapp_pool" "test" { func (r NetAppVolumeResource) networkTemplate(data acceptance.TestData) string { return fmt.Sprintf(` -%[1]s - resource "azurerm_virtual_network" "test" { - name = "acctest-VirtualNetwork-%[2]d" + 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"] + address_space = ["10.88.0.0/16"] tags = { "CreatedOnDate" = "2022-07-08T23:50:21Z", @@ -1464,10 +1439,10 @@ resource "azurerm_virtual_network" "test" { } resource "azurerm_subnet" "test-delegated" { - name = "acctest-Delegated-Subnet-%[2]d" + name = "acctest-Delegated-Subnet-%[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"] + address_prefixes = ["10.88.1.0/24"] delegation { name = "testdelegation" @@ -1480,12 +1455,12 @@ resource "azurerm_subnet" "test-delegated" { } resource "azurerm_subnet" "test-non-delegated" { - name = "acctest-Non-Delegated-Subnet-%[2]d" + name = "acctest-Non-Delegated-Subnet-%[1]d" resource_group_name = azurerm_resource_group.test.name virtual_network_name = azurerm_virtual_network.test.name - address_prefixes = ["10.6.0.0/24"] + address_prefixes = ["10.88.0.0/24"] } -`, r.templateProviderFeatureFlags(), data.RandomInteger) +`, data.RandomInteger) } func (NetAppVolumeResource) templateProviderFeatureFlags() string { @@ -1495,8 +1470,14 @@ provider "azurerm" { resource_group { prevent_deletion_if_contains_resources = false } - netapp { - prevent_volume_destruction = false + + key_vault { + purge_soft_delete_on_destroy = false + purge_soft_deleted_keys_on_destroy = false + } + + netapp { + prevent_volume_destruction = false delete_backups_on_backup_vault_destroy = true } } diff --git a/internal/services/netapp/registration.go b/internal/services/netapp/registration.go index 9b0e86bee0e8a..2bc1f82c58585 100644 --- a/internal/services/netapp/registration.go +++ b/internal/services/netapp/registration.go @@ -52,21 +52,23 @@ func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { // DataSources returns the typed DataSources supported by this service func (r Registration) DataSources() []sdk.DataSource { return []sdk.DataSource{ - NetAppVolumeGroupSapHanaDataSource{}, + NetAppVolumeGroupSAPHanaDataSource{}, NetAppVolumeQuotaRuleDataSource{}, NetAppAccountEncryptionDataSource{}, NetAppBackupVaultDataSource{}, NetAppBackupPolicyDataSource{}, + NetAppVolumeGroupOracleDataSource{}, } } // Resources returns the typed Resources supported by this service func (r Registration) Resources() []sdk.Resource { return []sdk.Resource{ - NetAppVolumeGroupSapHanaResource{}, + NetAppVolumeGroupSAPHanaResource{}, NetAppVolumeQuotaRuleResource{}, NetAppAccountEncryptionResource{}, NetAppBackupVaultResource{}, NetAppBackupPolicyResource{}, + NetAppVolumeGroupOracleResource{}, } } diff --git a/internal/services/netapp/validate/account_id.go b/internal/services/netapp/validate/account_id.go index b9dfdb5d7551a..aa9abc67c778a 100644 --- a/internal/services/netapp/validate/account_id.go +++ b/internal/services/netapp/validate/account_id.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2024-03-01/netappaccounts" ) +// ValidateNetAppAccountID validates the NetApp Account ID func ValidateNetAppAccountID(input interface{}, key string) (warnings []string, errors []error) { v, ok := input.(string) if !ok { diff --git a/internal/services/netapp/validate/validate_helper.go b/internal/services/netapp/validate/validate_helper.go new file mode 100644 index 0000000000000..56acb77edd214 --- /dev/null +++ b/internal/services/netapp/validate/validate_helper.go @@ -0,0 +1,33 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validate + +import ( + "strings" +) + +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 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_oracle_volumes_export_policy_validation_test.go b/internal/services/netapp/validate/volume_group_oracle_volumes_export_policy_validation_test.go new file mode 100644 index 0000000000000..00262f09e8765 --- /dev/null +++ b/internal/services/netapp/validate/volume_group_oracle_volumes_export_policy_validation_test.go @@ -0,0 +1,104 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validate + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2024-03-01/volumegroups" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func TestValidateNetAppVolumeGroupExportPolicyRuleOracle(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: pointer.To(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 := ValidateNetAppVolumeGroupExportPolicyRule(tc.Rule, tc.Protocol) + + if len(errors) != tc.Errors { + t.Fatalf("expected ValidateNetAppVolumeGroupOracleVolumes to return %d error(s) not %d", tc.Errors, len(errors)) + } + }) + } +} diff --git a/internal/services/netapp/validate/volume_group_oracle_volumes_validation.go b/internal/services/netapp/validate/volume_group_oracle_volumes_validation.go new file mode 100644 index 0000000000000..e869a9bd746d2 --- /dev/null +++ b/internal/services/netapp/validate/volume_group_oracle_volumes_validation.go @@ -0,0 +1,202 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validate + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2024-03-01/volumegroups" +) + +type VolumeSpecNameOracle string + +const ( + VolumeSpecNameOracleData1 VolumeSpecNameOracle = "ora-data1" + VolumeSpecNameOracleData2 VolumeSpecNameOracle = "ora-data2" + VolumeSpecNameOracleData3 VolumeSpecNameOracle = "ora-data3" + VolumeSpecNameOracleData4 VolumeSpecNameOracle = "ora-data4" + VolumeSpecNameOracleData5 VolumeSpecNameOracle = "ora-data5" + VolumeSpecNameOracleData6 VolumeSpecNameOracle = "ora-data6" + VolumeSpecNameOracleData7 VolumeSpecNameOracle = "ora-data7" + VolumeSpecNameOracleData8 VolumeSpecNameOracle = "ora-data8" + VolumeSpecNameOracleLog VolumeSpecNameOracle = "ora-log" + VolumeSpecNameOracleMirror VolumeSpecNameOracle = "ora-log-mirror" + VolumeSpecNameOracleBinary VolumeSpecNameOracle = "ora-binary" + VolumeSpecNameOracleBackup VolumeSpecNameOracle = "ora-backup" +) + +func PossibleValuesForVolumeSpecNameOracle() []string { + return []string{ + string(VolumeSpecNameOracleData1), + string(VolumeSpecNameOracleData2), + string(VolumeSpecNameOracleData3), + string(VolumeSpecNameOracleData4), + string(VolumeSpecNameOracleData5), + string(VolumeSpecNameOracleData6), + string(VolumeSpecNameOracleData7), + string(VolumeSpecNameOracleData8), + string(VolumeSpecNameOracleLog), + string(VolumeSpecNameOracleMirror), + string(VolumeSpecNameOracleBinary), + string(VolumeSpecNameOracleBackup), + } +} + +func RequiredVolumesForOracle() []string { + return []string{ + string(VolumeSpecNameOracleData1), + string(VolumeSpecNameOracleLog), + } +} + +func PossibleValuesForProtocolTypeVolumeGroupOracle() []string { + return []string{ + string(ProtocolTypeNfsV41), + string(ProtocolTypeNfsV3), + } +} + +func ValidateNetAppVolumeGroupOracleVolumes(volumeList *[]volumegroups.VolumeGroupVolumeProperties) []error { + errors := make([]error, 0) + expectedZone := "" + expectedPpgId := "" + expectedKeyVaultPrivateEndpointResourceId := "" + expectedEncryptionKeySource := "" + volumeSpecRepeatCount := make(map[string]int) + applicationType := string(volumegroups.ApplicationTypeORACLE) + + // Validating each volume + for _, volume := range pointer.From(volumeList) { + // Get protocol list + protocolTypeList := pointer.From(volume.Properties.ProtocolTypes) + + // Validate protocol list is not empty + if len(protocolTypeList) == 0 { + errors = append(errors, fmt.Errorf("'protocol type list cannot be empty'")) + } + + for _, protocol := range protocolTypeList { + // Validate protocol list does not contain invalid protocols + if !findStringInSlice(PossibleValuesForProtocolType(), protocol) { + errors = append(errors, fmt.Errorf("'protocol %v is invalid'", protocol)) + } + + // Validate that protocol is valid for Oracle + if !findStringInSlice(PossibleValuesForProtocolTypeVolumeGroupOracle(), protocol) { + errors = append(errors, fmt.Errorf("'protocol %v is invalid for Oracle'", protocol)) + } + + // Validating export policies + if volume.Properties.ExportPolicy != nil { + for _, rule := range pointer.From(volume.Properties.ExportPolicy.Rules) { + errors = append(errors, ValidateNetAppVolumeGroupExportPolicyRule(rule, protocol)...) + } + } + } + + // Checking CRR rule that log cannot be DataProtection type + if strings.EqualFold(pointer.From(volume.Properties.VolumeSpecName), string(VolumeSpecNameOracleLog)) && + 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 zone and proximity placement group are not specified together + if (pointer.From(volume.Zones) != nil || len(pointer.From(volume.Zones)) > 0) && pointer.From(volume.Properties.ProximityPlacementGroup) != "" { + errors = append(errors, fmt.Errorf("'zone and proximity_placement_group_id cannot be specified together on volume %v'", pointer.From(volume.Name))) + } + + // Getting the first zone for validations, all volumes must be in the same zone + if expectedZone == "" { + if volume.Zones != nil && len(pointer.From(volume.Zones)) > 0 { + expectedZone = pointer.From(volume.Zones)[0] + } + } + + // Validating that all volumes are in the same zone + if volume.Zones != nil && len(pointer.From(volume.Zones)) > 0 && pointer.From(volume.Zones)[0] != expectedZone { + errors = append(errors, fmt.Errorf("'zone must be the same on all volumes of this volume group, volume %v zone is %v'", pointer.From(volume.Name), pointer.From(volume.Zones)[0])) + } + + // Getting the first PPG for validations, all volumes must be in the same PPG + if expectedPpgId == "" { + if volume.Properties.ProximityPlacementGroup != nil { + expectedPpgId = pointer.From(volume.Properties.ProximityPlacementGroup) + } + } + + // Validating that all volumes are in the same PPG + if volume.Properties.ProximityPlacementGroup != nil && pointer.From(volume.Properties.ProximityPlacementGroup) != expectedPpgId { + errors = append(errors, fmt.Errorf("'proximity_placement_group_id must be the same on all volumes of this volume group, volume %v ppg id is %v'", pointer.From(volume.Name), pointer.From(volume.Properties.ProximityPlacementGroup))) + } + + // Validating that encryption_key_source when key source is AKV has key_vault_private_endpoint_id specified + if pointer.From(volume.Properties.EncryptionKeySource) == volumegroups.EncryptionKeySourceMicrosoftPointKeyVault && pointer.From(volume.Properties.KeyVaultPrivateEndpointResourceId) == "" { + errors = append(errors, fmt.Errorf("'encryption_key_source as microsoft.keyvault must have key_vault_private_endpoint_id specified on volume %v'", pointer.From(volume.Name))) + } + + // Validating that encryption_key_source is set when key_vault_private_endpoint_id is specified + if pointer.From(volume.Properties.KeyVaultPrivateEndpointResourceId) != "" && pointer.From(volume.Properties.EncryptionKeySource) == "" { + errors = append(errors, fmt.Errorf("'encryption_key_source must be set when key_vault_private_endpoint_id is specified on volume %v'", pointer.From(volume.Name))) + } + + // Getting the first KeyVaultPrivateEndpointResourceId for validations, all volumes must have the same KeyVaultPrivateEndpointResourceId + if expectedKeyVaultPrivateEndpointResourceId == "" { + if volume.Properties.KeyVaultPrivateEndpointResourceId != nil { + expectedKeyVaultPrivateEndpointResourceId = pointer.From(volume.Properties.KeyVaultPrivateEndpointResourceId) + } + } + + // Validating that all volumes have the same KeyVaultPrivateEndpointResourceId + if volume.Properties.KeyVaultPrivateEndpointResourceId != nil && pointer.From(volume.Properties.KeyVaultPrivateEndpointResourceId) != expectedKeyVaultPrivateEndpointResourceId { + errors = append(errors, fmt.Errorf("'key_vault_private_endpoint_id must be the same on all volumes of this volume group, volume %v key vault private endpoint id is %v'", pointer.From(volume.Name), pointer.From(volume.Properties.KeyVaultPrivateEndpointResourceId))) + } + + // Getting the first EncryptionKeySource for validations, all volumes must have the same EncryptionKeySource + if expectedEncryptionKeySource == "" { + if volume.Properties.EncryptionKeySource != nil { + expectedEncryptionKeySource = string(pointer.From(volume.Properties.EncryptionKeySource)) + } + } + + // Validating that all volumes have the same EncryptionKeySource + if volume.Properties.EncryptionKeySource != nil && pointer.From(volume.Properties.EncryptionKeySource) != volumegroups.EncryptionKeySource(expectedEncryptionKeySource) { + errors = append(errors, fmt.Errorf("'encryption_key_source must be the same on all volumes of this volume group, volume %v encryption key source is %v'", pointer.From(volume.Name), pointer.From(volume.Properties.EncryptionKeySource))) + } + + // Validate that volume networkFeature is set to standard when volume.Properties.EncryptionKeySource == "Microsoft.KeyVault" + if volume.Properties.EncryptionKeySource != nil && pointer.From(volume.Properties.EncryptionKeySource) == volumegroups.EncryptionKeySourceMicrosoftPointKeyVault && pointer.From(volume.Properties.NetworkFeatures) != volumegroups.NetworkFeaturesStandard { + errors = append(errors, fmt.Errorf("'network_feature must be set to standard when encryption_key_source is set to Microsoft.KeyVault on volume %v, current value is %v'", pointer.From(volume.Name), pointer.From(volume.Properties.NetworkFeatures))) + } + + // 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 RequiredVolumesForOracle() { + 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 +} diff --git a/internal/services/netapp/validate/volume_group_oracle_volumes_validation_test.go b/internal/services/netapp/validate/volume_group_oracle_volumes_validation_test.go new file mode 100644 index 0000000000000..ffa547558ae1a --- /dev/null +++ b/internal/services/netapp/validate/volume_group_oracle_volumes_validation_test.go @@ -0,0 +1,1453 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package validate + +import ( + "fmt" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2024-03-01/volumegroups" +) + +func TestValidateNetAppVolumeGroupOracleVolumes(t *testing.T) { + cases := []struct { + Name string + VolumesData []volumegroups.VolumeGroupVolumeProperties + Errors int + }{ + { + Name: "ValidateCorrectSettingsAllVolumesPPG", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + }, + }, + { // data2 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData2))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData2)), + }, + }, + { // data3 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData3))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData3)), + }, + }, + { // data4 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData4))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData4)), + }, + }, + { // data5 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData5))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData5)), + }, + }, + { // data6 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData6))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData6)), + }, + }, + { // data7 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData7))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData7)), + }, + }, + { // data8 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData8))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData8)), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleLog))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleLog)), + }, + }, + { // binary + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleBinary))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleBinary)), + }, + }, + { // mirror + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleMirror))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleMirror)), + }, + }, + { // backup + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleBackup))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleBackup)), + }, + }, + }, + Errors: 0, + }, + { + Name: "ValidateCorrectSettingsAllVolumesAvailabilityZone", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + }, + Zones: pointer.To([]string{"1"}), + }, + { // data2 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData2))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData2)), + }, + Zones: pointer.To([]string{"1"}), + }, + { // data3 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData3))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData3)), + }, + Zones: pointer.To([]string{"1"}), + }, + { // data4 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData4))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData4)), + }, + Zones: pointer.To([]string{"1"}), + }, + { // data5 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData5))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData5)), + }, + Zones: pointer.To([]string{"1"}), + }, + { // data6 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData6))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData6)), + }, + Zones: pointer.To([]string{"1"}), + }, + { // data7 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData7))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData7)), + }, + Zones: pointer.To([]string{"1"}), + }, + { // data8 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData8))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData8)), + }, + Zones: pointer.To([]string{"1"}), + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleLog))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleLog)), + }, + Zones: pointer.To([]string{"1"}), + }, + { // binary + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleBinary))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleBinary)), + }, + Zones: pointer.To([]string{"1"}), + }, + { // mirror + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleMirror))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleMirror)), + }, + Zones: pointer.To([]string{"1"}), + }, + { // backup + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleBackup))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleBackup)), + }, + Zones: pointer.To([]string{"1"}), + }, + }, + Errors: 0, + }, + { + Name: "ValidateCorrectSettingsAllVolumesProximityPlacementGroup", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + }, + }, + { // data2 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData2))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData2)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + }, + }, + { // data3 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData3))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData3)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + }, + }, + { // data4 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData4))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData4)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + }, + }, + { // data5 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData5))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData5)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + }, + }, + { // data6 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData6))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData6)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + }, + }, + { // data7 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData7))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData7)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + }, + }, + { // data8 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData8))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData8)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleLog))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleLog)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + }, + }, + { // binary + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleBinary))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleBinary)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + }, + }, + { // mirror + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleMirror))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleMirror)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + }, + }, + { // backup + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleBackup))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleBackup)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + }, + }, + }, + Errors: 0, + }, + { + Name: "ValidateCorrectSettingsAllVolumesProximityPlacementGroupFailsWhenDifferent", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg1"), + }, + }, + { // data2 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData2))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData2)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg2"), + }, + }, + { // data3 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData3))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData3)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg3"), + }, + }, + { // data4 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData4))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData4)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg4"), + }, + }, + { // data5 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData5))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData5)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg5"), + }, + }, + { // data6 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData6))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData6)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg6"), + }, + }, + { // data7 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData7))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData7)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg7"), + }, + }, + { // data8 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData8))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData8)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg8"), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleLog))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleLog)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg9"), + }, + }, + { // binary + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleBinary))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleBinary)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg10"), + }, + }, + { // mirror + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleMirror))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleMirror)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg11"), + }, + }, + { // backup + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleBackup))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleBackup)), + ProximityPlacementGroup: pointer.To("/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.Compute/proximityPlacementGroups/ppg12"), + }, + }, + }, + Errors: 11, + }, + { + Name: "ValidatePPGAndAvailabilityZoneNotSetAtSameTime", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + }, + Zones: pointer.To([]string{"1"}), + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleLog))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleLog)), + }, + Zones: pointer.To([]string{"1"}), + }, + }, + Errors: 2, + }, + { + Name: "ValidateMinimumVolumes", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleLog))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleLog)), + }, + }, + }, + Errors: 0, + }, + { + Name: "ValidateVolumesSameZone", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + }, + Zones: pointer.To([]string{"1"}), + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleLog))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleLog)), + }, + Zones: pointer.To([]string{"1"}), + }, + }, + Errors: 0, + }, + { + Name: "ValidateVolumesNotSameZoneErrors", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + }, + Zones: pointer.To([]string{"1"}), + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleLog))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleLog)), + }, + Zones: pointer.To([]string{"2"}), + }, + }, + Errors: 1, + }, + { + Name: "ValidateRequiredVolumeSpecs", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // mirror + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleMirror))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleMirror)), + }, + }, + { // binary + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleBinary))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(true), + }, + }, + }, + ProtocolTypes: pointer.To([]string{"NFSv4.1"}), + SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleBinary)), + }, + }, + }, + Errors: 2, + }, + { + Name: "ValidateLessThanMinimumVolumes", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + }, + }, + }, + Errors: 1, + }, + { + Name: "ValidateMultiProtocolFails", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(true), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + }, + }, + }, + Errors: 5, + }, + { + Name: "ValidateNoProtocolFails", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + }, + }, + }, + Errors: 2, + }, + { + Name: "ValidateInvalidProtocolList", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + }, + }, + }, + Errors: 3, + }, + { + Name: "ValidateInvalidProtocol", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + }, + }, + }, + Errors: 3, + }, + { + Name: "ValidateCIFSInvalidProtocolForOracle", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + Properties: volumegroups.VolumeProperties{ + ExportPolicy: &volumegroups.VolumePropertiesExportPolicy{ + Rules: &[]volumegroups.ExportPolicyRule{ + { + Nfsv3: pointer.To(false), + Nfsv41: pointer.To(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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + }, + }, + }, + Errors: 2, + }, + { + Name: "ValidateVolumeSpecCantRepeat", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleLog))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleLog)), + }, + }, + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + }, + }, + }, + Errors: 1, + }, + { + Name: "ValidateEndpointDstNotEnabledOnLogVolume", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleLog))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleLog)), + DataProtection: &volumegroups.VolumePropertiesDataProtection{ + Replication: &volumegroups.ReplicationObject{ + EndpointType: pointer.To(volumegroups.EndpointTypeDst), + }, + }, + }, + }, + }, + Errors: 1, + }, + { + Name: "ValidateCustomerManagedKeyOptionsAreSetTogether", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + EncryptionKeySource: pointer.To(volumegroups.EncryptionKeySourceMicrosoftPointKeyVault), + KeyVaultPrivateEndpointResourceId: pointer.To("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/myResourceGroup/providers/Microsoft.Network/privateEndpoints/myKeyvaultPE"), + NetworkFeatures: pointer.To(volumegroups.NetworkFeaturesStandard), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleLog))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleLog)), + EncryptionKeySource: pointer.To(volumegroups.EncryptionKeySourceMicrosoftPointKeyVault), + KeyVaultPrivateEndpointResourceId: pointer.To("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/myResourceGroup/providers/Microsoft.Network/privateEndpoints/myKeyvaultPE"), + NetworkFeatures: pointer.To(volumegroups.NetworkFeaturesStandard), + }, + }, + }, + Errors: 0, + }, + { + Name: "ValidateCustomerManagedKeyOptionsNotSetTogetherFailsWhenKeyVault", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + EncryptionKeySource: pointer.To(volumegroups.EncryptionKeySourceMicrosoftPointKeyVault), + NetworkFeatures: pointer.To(volumegroups.NetworkFeaturesStandard), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleLog))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleLog)), + KeyVaultPrivateEndpointResourceId: pointer.To("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/myResourceGroup/providers/Microsoft.Network/privateEndpoints/myKeyvaultPE"), + NetworkFeatures: pointer.To(volumegroups.NetworkFeaturesStandard), + }, + }, + }, + Errors: 2, + }, + { + Name: "ValidateCustomerManagedKeyNotSameAllVolumesFails", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + EncryptionKeySource: pointer.To(volumegroups.EncryptionKeySourceMicrosoftPointKeyVault), + KeyVaultPrivateEndpointResourceId: pointer.To("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/myResourceGroup/providers/Microsoft.Network/privateEndpoints/myKeyvaultPE"), + NetworkFeatures: pointer.To(volumegroups.NetworkFeaturesStandard), + }, + }, + { // data2 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData2))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData2)), + EncryptionKeySource: pointer.To(volumegroups.EncryptionKeySourceMicrosoftPointNetApp), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleLog))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleLog)), + EncryptionKeySource: pointer.To(volumegroups.EncryptionKeySourceMicrosoftPointKeyVault), + KeyVaultPrivateEndpointResourceId: pointer.To("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/myResourceGroup/providers/Microsoft.Network/privateEndpoints/myKeyvaultPE1"), + NetworkFeatures: pointer.To(volumegroups.NetworkFeaturesStandard), + }, + }, + }, + Errors: 2, + }, + { + Name: "ValidatePlatformManagedKey", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + EncryptionKeySource: pointer.To(volumegroups.EncryptionKeySourceMicrosoftPointNetApp), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleLog))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleLog)), + EncryptionKeySource: pointer.To(volumegroups.EncryptionKeySourceMicrosoftPointNetApp), + }, + }, + }, + Errors: 0, + }, + { + Name: "ValidateCustomerManagedKeyFailsOnBasicNetworkFeatures", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + EncryptionKeySource: pointer.To(volumegroups.EncryptionKeySourceMicrosoftPointKeyVault), + KeyVaultPrivateEndpointResourceId: pointer.To("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/myResourceGroup/providers/Microsoft.Network/privateEndpoints/myKeyvaultPE"), + NetworkFeatures: pointer.To(volumegroups.NetworkFeaturesBasic), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleLog))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleLog)), + EncryptionKeySource: pointer.To(volumegroups.EncryptionKeySourceMicrosoftPointKeyVault), + KeyVaultPrivateEndpointResourceId: pointer.To("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/myResourceGroup/providers/Microsoft.Network/privateEndpoints/myKeyvaultPE"), + }, + }, + }, + Errors: 2, + }, + { + Name: "ValidateCustomerManagedKeyPassOnStandardNetworkFeatures", + VolumesData: []volumegroups.VolumeGroupVolumeProperties{ + { // data1 + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleData1))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleData1)), + EncryptionKeySource: pointer.To(volumegroups.EncryptionKeySourceMicrosoftPointKeyVault), + KeyVaultPrivateEndpointResourceId: pointer.To("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/myResourceGroup/providers/Microsoft.Network/privateEndpoints/myKeyvaultPE"), + NetworkFeatures: pointer.To(volumegroups.NetworkFeaturesStandard), + }, + }, + { // log + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameOracleLog))), + 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.SecurityStyleUnix), + VolumeSpecName: pointer.To(string(VolumeSpecNameOracleLog)), + EncryptionKeySource: pointer.To(volumegroups.EncryptionKeySourceMicrosoftPointKeyVault), + KeyVaultPrivateEndpointResourceId: pointer.To("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/myResourceGroup/providers/Microsoft.Network/privateEndpoints/myKeyvaultPE"), + NetworkFeatures: pointer.To(volumegroups.NetworkFeaturesStandard), + }, + }, + }, + Errors: 0, + }, + } + + for _, tc := range cases { + t.Run(tc.Name, func(t *testing.T) { + errors := ValidateNetAppVolumeGroupOracleVolumes(pointer.To(tc.VolumesData)) + + if len(errors) != tc.Errors { + t.Fatalf("expected ValidateNetAppVolumeGroupOracleVolumes to return %d error(s) not %d\nError List: \n%v", tc.Errors, len(errors), 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 index c2416e3d8aca3..4c7437b37a70f 100644 --- 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 @@ -11,7 +11,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/utils" ) -func TestValidateNetAppVolumeGroupExportPolicyRuleSAPHanna(t *testing.T) { +func TestValidateNetAppVolumeGroupExportPolicyRuleSAPHana(t *testing.T) { cases := []struct { Name string Protocol string @@ -94,7 +94,7 @@ func TestValidateNetAppVolumeGroupExportPolicyRuleSAPHanna(t *testing.T) { for _, tc := range cases { t.Run(tc.Name, func(t *testing.T) { - errors := ValidateNetAppVolumeGroupExportPolicyRuleSAPHanna(tc.Rule, tc.Protocol) + errors := ValidateNetAppVolumeGroupExportPolicyRule(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 index be863b92a9b82..183a497abf2df 100644 --- a/internal/services/netapp/validate/volume_group_sap_hana_volumes_validation.go +++ b/internal/services/netapp/validate/volume_group_sap_hana_volumes_validation.go @@ -11,50 +11,34 @@ import ( "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2024-03-01/volumegroups" ) -type VolumeSpecNameSapHana string +type VolumeSpecNameSAPHana string const ( - VolumeSpecNameSapHanaData VolumeSpecNameSapHana = "data" - VolumeSpecNameSapHanaLog VolumeSpecNameSapHana = "log" - VolumeSpecNameSapHanaShared VolumeSpecNameSapHana = "shared" - VolumeSpecNameSapHanaDataBackup VolumeSpecNameSapHana = "data-backup" - VolumeSpecNameSapHanaLogBackup VolumeSpecNameSapHana = "log-backup" + VolumeSpecNameSAPHanaData VolumeSpecNameSAPHana = "data" + VolumeSpecNameSAPHanaLog VolumeSpecNameSAPHana = "log" + VolumeSpecNameSAPHanaShared VolumeSpecNameSAPHana = "shared" + VolumeSpecNameSAPHanaDataBackup VolumeSpecNameSAPHana = "data-backup" + VolumeSpecNameSAPHanaLogBackup VolumeSpecNameSAPHana = "log-backup" ) -func PossibleValuesForVolumeSpecNameSapHana() []string { +func PossibleValuesForVolumeSpecNameSAPHana() []string { return []string{ - string(VolumeSpecNameSapHanaData), - string(VolumeSpecNameSapHanaLog), - string(VolumeSpecNameSapHanaShared), - string(VolumeSpecNameSapHanaDataBackup), - string(VolumeSpecNameSapHanaLogBackup), + string(VolumeSpecNameSAPHanaData), + string(VolumeSpecNameSAPHanaLog), + string(VolumeSpecNameSAPHanaShared), + string(VolumeSpecNameSAPHanaDataBackup), + string(VolumeSpecNameSAPHanaLogBackup), } } func RequiredVolumesForSAPHANA() []string { return []string{ - string(VolumeSpecNameSapHanaData), - string(VolumeSpecNameSapHanaLog), + 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 { +func PossibleValuesForProtocolTypeVolumeGroupSAPHana() []string { return []string{ string(ProtocolTypeNfsV41), string(ProtocolTypeNfsV3), @@ -66,61 +50,47 @@ func ValidateNetAppVolumeGroupSAPHanaVolumes(volumeList *[]volumegroups.VolumeGr 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)) + for _, protocolType := range protocolTypeList { + // 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)) - } + // 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))) - } + // 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)...) + // Validating export policies + if volume.Properties.ExportPolicy != nil { + for _, rule := range pointer.From(volume.Properties.ExportPolicy.Rules) { + errors = append(errors, ValidateNetAppVolumeGroupExportPolicyRule(rule, protocolType)...) + } } } // Checking CRR rule that log cannot be DataProtection type - if strings.EqualFold(pointer.From(volume.Properties.VolumeSpecName), string(VolumeSpecNameSapHanaLog)) && + 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)) { @@ -135,16 +105,16 @@ func ValidateNetAppVolumeGroupSAPHanaVolumes(volumeList *[]volumegroups.VolumeGr } // 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))) && + if (strings.EqualFold(pointer.From(volume.Properties.VolumeSpecName), string(VolumeSpecNameSAPHanaDataBackup)) || + strings.EqualFold(pointer.From(volume.Properties.VolumeSpecName), string(VolumeSpecNameSAPHanaLogBackup))) && pointer.From(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))) && + 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))) && pointer.From(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))) } @@ -169,12 +139,3 @@ func ValidateNetAppVolumeGroupSAPHanaVolumes(volumeList *[]volumegroups.VolumeGr 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 index a01d28537ac9e..671a533660b65 100644 --- 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 @@ -9,7 +9,6 @@ import ( "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2024-03-01/volumegroups" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) func TestValidateNetAppVolumeGroupSAPHanaVolumes(t *testing.T) { @@ -22,86 +21,86 @@ func TestValidateNetAppVolumeGroupSAPHanaVolumes(t *testing.T) { Name: "ValidateCorrectSettingsAllVolumes", VolumesData: []volumegroups.VolumeGroupVolumeProperties{ { // data - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + 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), + Nfsv41: pointer.To(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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaData)), }, }, { // log - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLog))), + 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), + Nfsv41: pointer.To(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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLog)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaLog)), }, }, { // shared - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaShared))), + 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), + Nfsv41: pointer.To(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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaShared)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaShared)), }, }, { // data-backup - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaDataBackup))), + 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), + Nfsv41: pointer.To(true), }, }, }, ProtocolTypes: pointer.To([]string{"NFSv4.1"}), SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaDataBackup)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaDataBackup)), }, }, { // log-backup - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLogBackup))), + 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), + Nfsv41: pointer.To(true), }, }, }, ProtocolTypes: pointer.To([]string{"NFSv4.1"}), SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLogBackup)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaLogBackup)), }, }, }, @@ -111,37 +110,37 @@ func TestValidateNetAppVolumeGroupSAPHanaVolumes(t *testing.T) { Name: "ValidateMinimumVolumes", VolumesData: []volumegroups.VolumeGroupVolumeProperties{ { // data - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + 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), + Nfsv41: pointer.To(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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaData)), }, }, { // log - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLog))), + 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), + Nfsv41: pointer.To(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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLog)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaLog)), }, }, }, @@ -151,52 +150,52 @@ func TestValidateNetAppVolumeGroupSAPHanaVolumes(t *testing.T) { Name: "ValidateRequiredVolumeSpecs", VolumesData: []volumegroups.VolumeGroupVolumeProperties{ { // shared - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaShared))), + 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), + Nfsv41: pointer.To(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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaShared)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaShared)), }, }, { // data-backup - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaDataBackup))), + 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), + Nfsv41: pointer.To(true), }, }, }, ProtocolTypes: pointer.To([]string{"NFSv4.1"}), SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaDataBackup)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaDataBackup)), }, }, { // log-backup - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLogBackup))), + 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), + Nfsv41: pointer.To(true), }, }, }, ProtocolTypes: pointer.To([]string{"NFSv4.1"}), SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLogBackup)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaLogBackup)), }, }, }, @@ -206,165 +205,165 @@ func TestValidateNetAppVolumeGroupSAPHanaVolumes(t *testing.T) { Name: "ValidateLessThanMinimumVolumes", VolumesData: []volumegroups.VolumeGroupVolumeProperties{ { // data - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + 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), + Nfsv41: pointer.To(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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaData)), }, }, }, - Errors: 2, + Errors: 1, }, { Name: "ValidateMultiProtocolFails", VolumesData: []volumegroups.VolumeGroupVolumeProperties{ { // data - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + 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), + Nfsv3: pointer.To(true), + Nfsv41: pointer.To(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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaData)), }, }, }, - Errors: 3, + Errors: 6, }, { Name: "ValidateNoProtocolFails", VolumesData: []volumegroups.VolumeGroupVolumeProperties{ { // data - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + 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), + Nfsv41: pointer.To(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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaData)), }, }, }, - Errors: 4, + Errors: 2, }, { Name: "ValidateInvalidProtocolList", VolumesData: []volumegroups.VolumeGroupVolumeProperties{ { // data - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + 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), + Nfsv41: pointer.To(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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaData)), }, }, }, - Errors: 3, + Errors: 4, }, { Name: "ValidateInvalidProtocol", VolumesData: []volumegroups.VolumeGroupVolumeProperties{ { // data - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + 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), + Nfsv41: pointer.To(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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaData)), }, }, }, - Errors: 4, + Errors: 3, }, { Name: "ValidateCIFSInvalidProtocolForSAPHana", VolumesData: []volumegroups.VolumeGroupVolumeProperties{ { // data - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + 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), + Nfsv41: pointer.To(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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaData)), }, }, }, - Errors: 3, + Errors: 2, }, { Name: "ValidateNoNfsVersionThreeOnDataLogAndSharedVolumes", VolumesData: []volumegroups.VolumeGroupVolumeProperties{ { // data - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + 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)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaData)), }, }, { // log - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLog))), + 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)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaLog)), }, }, { // shared - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaShared))), + 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)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaShared)), }, }, }, @@ -374,37 +373,37 @@ func TestValidateNetAppVolumeGroupSAPHanaVolumes(t *testing.T) { Name: "ValidateNoPPGBackupVolumes", VolumesData: []volumegroups.VolumeGroupVolumeProperties{ { // data - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + 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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaData)), }, }, { // log - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLog))), + 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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLog)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaLog)), }, }, { // data-backup - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaDataBackup))), + 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)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaDataBackup)), }, }, { // log-backup - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLogBackup))), + 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)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaLogBackup)), }, }, }, @@ -414,27 +413,27 @@ func TestValidateNetAppVolumeGroupSAPHanaVolumes(t *testing.T) { Name: "ValidateRequiredPpgForNonBackupVolumes", VolumesData: []volumegroups.VolumeGroupVolumeProperties{ { // data - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSAPHanaData))), Properties: volumegroups.VolumeProperties{ ProtocolTypes: pointer.To([]string{"NFSv4.1"}), SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaData)), }, }, { // log - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLog))), + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSAPHanaLog))), Properties: volumegroups.VolumeProperties{ ProtocolTypes: pointer.To([]string{"NFSv4.1"}), SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLog)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaLog)), }, }, { // shared - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaShared))), + Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSAPHanaShared))), Properties: volumegroups.VolumeProperties{ ProtocolTypes: pointer.To([]string{"NFSv4.1"}), SecurityStyle: pointer.To(volumegroups.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaShared)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaShared)), }, }, }, @@ -444,30 +443,30 @@ func TestValidateNetAppVolumeGroupSAPHanaVolumes(t *testing.T) { Name: "ValidateVolumeSpecCantRepeat", VolumesData: []volumegroups.VolumeGroupVolumeProperties{ { // data - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + 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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaData)), }, }, { // log - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLog))), + 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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLog)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaLog)), }, }, { // shared - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaShared))), + 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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaData)), }, }, }, @@ -477,21 +476,21 @@ func TestValidateNetAppVolumeGroupSAPHanaVolumes(t *testing.T) { Name: "ValidateEndpointDstNotEnabledOnLogVolume", VolumesData: []volumegroups.VolumeGroupVolumeProperties{ { // data - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + 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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaData)), }, }, { // log - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLog))), + 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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLog)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaLog)), DataProtection: &volumegroups.VolumePropertiesDataProtection{ Replication: &volumegroups.ReplicationObject{ EndpointType: pointer.To(volumegroups.EndpointTypeDst), @@ -506,12 +505,12 @@ func TestValidateNetAppVolumeGroupSAPHanaVolumes(t *testing.T) { Name: "ValidateSnapshotPolicyNotEnabledOnEndpointDstVolume", VolumesData: []volumegroups.VolumeGroupVolumeProperties{ { // data - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaData))), + 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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaData)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaData)), DataProtection: &volumegroups.VolumePropertiesDataProtection{ Replication: &volumegroups.ReplicationObject{ EndpointType: pointer.To(volumegroups.EndpointTypeDst), @@ -523,12 +522,12 @@ func TestValidateNetAppVolumeGroupSAPHanaVolumes(t *testing.T) { }, }, { // log - Name: pointer.To(fmt.Sprintf("volume-%v", string(VolumeSpecNameSapHanaLog))), + 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.SecurityStyleUnix), - VolumeSpecName: pointer.To(string(VolumeSpecNameSapHanaLog)), + VolumeSpecName: pointer.To(string(VolumeSpecNameSAPHanaLog)), }, }, }, diff --git a/internal/services/netapp/validate/volume_group_sap_hana_volumes_export_policy_validation.go b/internal/services/netapp/validate/volume_group_volumes_export_policy_validation.go similarity index 91% rename from internal/services/netapp/validate/volume_group_sap_hana_volumes_export_policy_validation.go rename to internal/services/netapp/validate/volume_group_volumes_export_policy_validation.go index 1adfea70124ea..d8058b079279b 100644 --- a/internal/services/netapp/validate/volume_group_sap_hana_volumes_export_policy_validation.go +++ b/internal/services/netapp/validate/volume_group_volumes_export_policy_validation.go @@ -11,7 +11,7 @@ import ( "github.com/hashicorp/go-azure-sdk/resource-manager/netapp/2024-03-01/volumegroups" ) -func ValidateNetAppVolumeGroupExportPolicyRuleSAPHanna(rule volumegroups.ExportPolicyRule, protocolType string) []error { +func ValidateNetAppVolumeGroupExportPolicyRule(rule volumegroups.ExportPolicyRule, protocolType string) []error { errors := make([]error, 0) // Validating that nfsv3 and nfsv4.1 are not enabled in the same rule diff --git a/website/docs/d/netapp_volume_group_oracle.html.markdown b/website/docs/d/netapp_volume_group_oracle.html.markdown new file mode 100644 index 0000000000000..50db9f03e26db --- /dev/null +++ b/website/docs/d/netapp_volume_group_oracle.html.markdown @@ -0,0 +1,139 @@ +--- +subcategory: "NetApp" +layout: "azurerm" +page_title: "Azure Resource Manager: Data Source: azurerm_netapp_volume_group_oracle" +description: |- + Gets information about an existing Application Volume Group for Oracle application. +--- + +# Data Source: azurerm_netapp_volume_group_oracle + +Use this data source to access information about an existing Application Volume Group for Oracle application. + +## Example Usage + +```hcl +data "azurerm_netapp_volume_group_oracle" "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_oracle.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 Oracle 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_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. + +* `network_features` - Network feature in use at the time of volume creation. + +* `encryption_key_source` - The encryption key source. + +* `key_vault_private_endpoint_id` - The Private Endpoint ID for Key Vault when using customer managed keys. + +--- + +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_oracle.html.markdown b/website/docs/r/netapp_volume_group_oracle.html.markdown new file mode 100644 index 0000000000000..d3d40c68ec430 --- /dev/null +++ b/website/docs/r/netapp_volume_group_oracle.html.markdown @@ -0,0 +1,247 @@ +--- +subcategory: "NetApp" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_netapp_volume_group_oracle" +description: |- + Manages a Application Volume Group for Oracle application. +--- + +# azurerm_netapp_volume_group_oracle + +Manages a Application Volume Group for Oracle application. + +~> **NOTE**: This feature is intended to be used for Oracle workloads only, with several requirements, please refer to [Understand Azure NetApp Files application volume group for Oracle](https://learn.microsoft.com/en-us/azure/azure-netapp-files/application-volume-oracle-introduction) document as the starting point to understand this feature before using it with Terraform. + +## Example Usage + +```hcl +provider "azurerm" { + features { + netapp { + prevent_volume_destruction = true + } + } +} + +resource "azurerm_resource_group" "example" { + name = "${var.prefix}-resources" + location = var.location + + tags = { + "SkipNRMSNSG" = "true" + } +} + +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.88.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.88.2.0/24"] + + delegation { + name = "exampledelegation" + + service_delegation { + name = "Microsoft.Netapp/volumes" + actions = ["Microsoft.Network/networkinterfaces/*", "Microsoft.Network/virtualNetworks/subnets/join/action"] + } + } +} + +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 + ] +} + +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 = 4 + qos_type = "Manual" +} + + +resource "azurerm_netapp_volume_group_oracle" "example" { + name = "${var.prefix}-NetAppVolumeGroupOracle" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + account_name = azurerm_netapp_account.example.name + group_description = "Example volume group for Oracle" + application_identifier = "TST" + + volume { + name = "${var.prefix}-volume-ora1" + volume_path = "${var.prefix}-my-unique-file-ora-path-1" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.example.id + subnet_id = azurerm_subnet.example.id + zone = "1" + volume_spec_name = "ora-data1" + 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 + } + } + + volume { + name = "${var.prefix}-volume-oraLog" + volume_path = "${var.prefix}-my-unique-file-oralog-path" + service_level = "Standard" + capacity_pool_id = azurerm_netapp_pool.example.id + subnet_id = azurerm_subnet.example.id + zone = "1" + volume_spec_name = "ora-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 + } + } +} +``` + +## 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. `OR1`. 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`. + +* `proximity_placement_group_id` - (Optional) The ID of the proximity placement group (PPG). Changing this forces a new Application Volume Group to be created and data will be lost. + +~> **NOTE**: For Oracle 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 Oracle](https://learn.microsoft.com/en-us/azure/azure-netapp-files/application-volume-group-oracle-considerations) for details and other requirements. Note that this cannot be used together with `zone`. + +* `zone` - (Optional) Specifies the Availability Zone in which the Volume should be located. Possible values are `1`, `2` and `3`, depending on the Azure region. Changing this forces a new resource to be created. This feature is currently in preview, for more information on how to enable it, please refer to [Manage availability zone volume placement for Azure NetApp Files](https://learn.microsoft.com/en-us/azure/azure-netapp-files/manage-availability-zone-volume-placement). Note that this cannot be used together with `proximity_placement_group_id`. + +* `security_style` - (Required) Volume security style. Possible values are `ntfs` and `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 `ora-data1` through `ora-data8`, `ora-log`, `ora-log-mirror`, `ora-backup` and `ora-binary`. 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. + +* `network_features` - (Optional) Indicates which network feature to use, accepted values are `Basic` or `Standard`, it defaults to `Basic` if not defined. This is a feature in public preview and for more information about it and how to register, please refer to [Configure network features for an Azure NetApp Files volume](https://docs.microsoft.com/en-us/azure/azure-netapp-files/configure-network-features). This is required if enabling customer managed keys encryption scenario. + +* `encryption_key_source` - (Optional) The encryption key source, it can be `Microsoft.NetApp` for platform managed keys or `Microsoft.KeyVault` for customer-managed keys. This is required with `key_vault_private_endpoint_id`. Changing this forces a new resource to be created. + +* `key_vault_private_endpoint_id` - (Optional) The Private Endpoint ID for Key Vault, which is required when using customer-managed keys. This is required with `encryption_key_source`. Changing this forces a new resource to be created. + +* `export_policy_rule` - (Required) One or more `export_policy_rule` blocks as defined below. + +* `data_protection_snapshot_policy` - (Optional) A `data_protection_snapshot_policy` block as defined below. + +--- +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 90 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_oracle.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mytest-rg/providers/Microsoft.NetApp/netAppAccounts/netapp-account-test/volumeGroups/netapp-volumegroup-test +``` diff --git a/website/docs/r/netapp_volume_group_sap_hana.html.markdown b/website/docs/r/netapp_volume_group_sap_hana.html.markdown index 178b502ffdd55..9616d45de6a20 100644 --- a/website/docs/r/netapp_volume_group_sap_hana.html.markdown +++ b/website/docs/r/netapp_volume_group_sap_hana.html.markdown @@ -42,14 +42,14 @@ 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"] + address_space = ["10.88.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"] + address_prefixes = ["10.88.2.0/24"] delegation { name = "testdelegation" @@ -65,7 +65,7 @@ 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"] + address_prefixes = ["10.88.1.0/24"] } resource "azurerm_proximity_placement_group" "example" { diff --git a/website/docs/r/vmware_netapp_volume_attachment.html.markdown b/website/docs/r/vmware_netapp_volume_attachment.html.markdown index cc05170ddeaf1..72dfaa8d32f44 100644 --- a/website/docs/r/vmware_netapp_volume_attachment.html.markdown +++ b/website/docs/r/vmware_netapp_volume_attachment.html.markdown @@ -37,14 +37,14 @@ resource "azurerm_virtual_network" "test" { name = "example-VirtualNetwork" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name - address_space = ["10.6.0.0/16"] + address_space = ["10.88.0.0/16"] } resource "azurerm_subnet" "netappSubnet" { name = "example-Subnet" resource_group_name = azurerm_resource_group.test.name virtual_network_name = azurerm_virtual_network.test.name - address_prefixes = ["10.6.2.0/24"] + address_prefixes = ["10.88.2.0/24"] delegation { name = "testdelegation" @@ -60,7 +60,7 @@ resource "azurerm_subnet" "gatewaySubnet" { name = "GatewaySubnet" resource_group_name = azurerm_resource_group.test.name virtual_network_name = azurerm_virtual_network.test.name - address_prefixes = ["10.6.1.0/24"] + address_prefixes = ["10.88.1.0/24"] } resource "azurerm_virtual_network_gateway" "test" {