From 9e1173cc9010573fbf6f9b2a6a76fa78dfd8e2a6 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Mon, 27 May 2024 10:32:45 +0000 Subject: [PATCH 1/3] new datasource elastic_san_volume_group --- .../elastic_san_volume_group_data_source.go | 171 ++++++++++++++++++ ...astic_san_volume_group_data_source_test.go | 49 +++++ internal/services/elasticsan/registration.go | 1 + .../d/elastic_san_volume_group.html.markdown | 93 ++++++++++ 4 files changed, 314 insertions(+) create mode 100644 internal/services/elasticsan/elastic_san_volume_group_data_source.go create mode 100644 internal/services/elasticsan/elastic_san_volume_group_data_source_test.go create mode 100644 website/docs/d/elastic_san_volume_group.html.markdown diff --git a/internal/services/elasticsan/elastic_san_volume_group_data_source.go b/internal/services/elasticsan/elastic_san_volume_group_data_source.go new file mode 100644 index 000000000000..63f05e48d8e8 --- /dev/null +++ b/internal/services/elasticsan/elastic_san_volume_group_data_source.go @@ -0,0 +1,171 @@ +package elasticsan + +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/identity" + "github.com/hashicorp/go-azure-sdk/resource-manager/elasticsan/2023-01-01/volumegroups" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/elasticsan/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +type ElasticSANVolumeGroupDataSource struct{} + +var _ sdk.DataSource = ElasticSANVolumeGroupDataSource{} + +type ElasticSANVolumeGroupDataSourceModel struct { + SanId string `tfschema:"elastic_san_id"` + EncryptionType string `tfschema:"encryption_type"` + Encryption []ElasticSANVolumeGroupResourceEncryptionModel `tfschema:"encryption"` + Identity []identity.ModelSystemAssignedUserAssigned `tfschema:"identity"` + Name string `tfschema:"name"` + NetworkRule []ElasticSANVolumeGroupResourceNetworkRuleModel `tfschema:"network_rule"` + ProtocolType string `tfschema:"protocol_type"` +} + +func (r ElasticSANVolumeGroupDataSource) ResourceType() string { + return "azurerm_elastic_san_volume_group" +} + +func (r ElasticSANVolumeGroupDataSource) ModelObject() interface{} { + return &ElasticSANVolumeGroupDataSourceModel{} +} + +func (r ElasticSANVolumeGroupDataSource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.ElasticSanVolumeGroupName, + }, + + "elastic_san_id": commonschema.ResourceIDReferenceRequired(&volumegroups.ElasticSanId{}), + } +} + +func (r ElasticSANVolumeGroupDataSource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "encryption_type": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "encryption": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "key_vault_key_id": { + Computed: true, + Type: pluginsdk.TypeString, + }, + "user_assigned_identity_id": { + Computed: true, + Type: pluginsdk.TypeString, + }, + "current_versioned_key_expiration_timestamp": { + Computed: true, + Type: pluginsdk.TypeString, + }, + "current_versioned_key_id": { + Computed: true, + Type: pluginsdk.TypeString, + }, + "last_key_rotation_timestamp": { + Computed: true, + Type: pluginsdk.TypeString, + }, + }, + }, + }, + + "network_rule": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "subnet_id": { + Computed: true, + Type: pluginsdk.TypeString, + }, + "action": { + Computed: true, + Type: pluginsdk.TypeString, + }, + }, + }, + }, + + "protocol_type": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "identity": commonschema.SystemOrUserAssignedIdentityComputed(), + } +} + +func (r ElasticSANVolumeGroupDataSource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.ElasticSan.VolumeGroups + + var state ElasticSANVolumeGroupDataSourceModel + if err := metadata.Decode(&state); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + elasticSanId, err := volumegroups.ParseElasticSanID(state.SanId) + if err != nil { + return err + } + + id := volumegroups.NewVolumeGroupID(elasticSanId.SubscriptionId, elasticSanId.ResourceGroupName, elasticSanId.ElasticSanName, state.Name) + + resp, err := client.Get(ctx, id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return fmt.Errorf("%s does not exist", id) + } + + return fmt.Errorf("retrieving %s: %+v", id, err) + } + + if model := resp.Model; model != nil { + state.SanId = elasticSanId.ID() + state.Name = id.VolumeGroupName + + flattenedIdentity, err := identity.FlattenSystemOrUserAssignedMapToModel(model.Identity) + if err != nil { + return fmt.Errorf("flattening identity: %+v", err) + } + state.Identity = *flattenedIdentity + + if model.Properties != nil { + state.EncryptionType = string(pointer.From(model.Properties.Encryption)) + state.NetworkRule = FlattenVolumeGroupNetworkRules(model.Properties.NetworkAcls) + + if model.Properties.ProtocolType != nil { + state.ProtocolType = string(pointer.From(model.Properties.ProtocolType)) + } + + state.Encryption, err = FlattenVolumeGroupEncryption(model.Properties.EncryptionProperties) + if err != nil { + return fmt.Errorf("flattening encryption: %+v", err) + } + } + } + + metadata.SetID(id) + + return metadata.Encode(&state) + }, + } +} diff --git a/internal/services/elasticsan/elastic_san_volume_group_data_source_test.go b/internal/services/elasticsan/elastic_san_volume_group_data_source_test.go new file mode 100644 index 000000000000..62b4da1b5241 --- /dev/null +++ b/internal/services/elasticsan/elastic_san_volume_group_data_source_test.go @@ -0,0 +1,49 @@ +package elasticsan_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type ElasticSANVolumeGroupDataSource struct{} + +func TestAccElasticSANVolumeGroupDataSource_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_elastic_san_volume_group", "test") + d := ElasticSANVolumeGroupDataSource{} + + data.DataSourceTestInSequence(t, []acceptance.TestStep{ + { + Config: d.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("encryption_type").HasValue("EncryptionAtRestWithCustomerManagedKey"), + check.That(data.ResourceName).Key("protocol_type").HasValue("Iscsi"), + check.That(data.ResourceName).Key("encryption.#").HasValue("1"), + check.That(data.ResourceName).Key("encryption.0.key_vault_key_id").IsNotEmpty(), + check.That(data.ResourceName).Key("encryption.0.user_assigned_identity_id").IsNotEmpty(), + check.That(data.ResourceName).Key("encryption.0.current_versioned_key_expiration_timestamp").IsNotEmpty(), + check.That(data.ResourceName).Key("encryption.0.current_versioned_key_id").IsNotEmpty(), + check.That(data.ResourceName).Key("encryption.0.last_key_rotation_timestamp").IsNotEmpty(), + check.That(data.ResourceName).Key("identity.#").HasValue("1"), + check.That(data.ResourceName).Key("identity.0.type").HasValue("UserAssigned"), + check.That(data.ResourceName).Key("identity.0.identity_ids.#").HasValue("1"), + check.That(data.ResourceName).Key("network_rule.#").HasValue("2"), + check.That(data.ResourceName).Key("network_rule.0.subnet_id").IsNotEmpty(), + check.That(data.ResourceName).Key("network_rule.0.action").HasValue("Allow"), + ), + }, + }) +} + +func (d ElasticSANVolumeGroupDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_elastic_san_volume_group" "test" { + name = azurerm_elastic_san_volume_group.test.nameelastic_san_id + elastic_san_id = azurerm_elastic_san_volume_group.test.elastic_san_id +} +`, ElasticSANVolumeGroupTestResource{}.complete(data)) +} diff --git a/internal/services/elasticsan/registration.go b/internal/services/elasticsan/registration.go index d5d8f727a61a..2b0e463cbb78 100644 --- a/internal/services/elasticsan/registration.go +++ b/internal/services/elasticsan/registration.go @@ -20,6 +20,7 @@ func (Registration) Name() string { func (Registration) DataSources() []sdk.DataSource { return []sdk.DataSource{ ElasticSANDataSource{}, + ElasticSANVolumeGroupDataSource{}, } } diff --git a/website/docs/d/elastic_san_volume_group.html.markdown b/website/docs/d/elastic_san_volume_group.html.markdown new file mode 100644 index 000000000000..eac340c03e49 --- /dev/null +++ b/website/docs/d/elastic_san_volume_group.html.markdown @@ -0,0 +1,93 @@ +--- +subcategory: "Elastic SAN" +layout: "azurerm" +page_title: "Azure Resource Manager: Data Source: azurerm_elastic_san_volume_group" +description: |- + Gets information about an existing Elastic SAN Volume Group. +--- + +# Data Source: azurerm_elastic_san_volume_group + +Use this data source to access information about an existing Elastic SAN Volume Group. + +## Example Usage + +```hcl +data "azurerm_elastic_san" "example" { + name = "existing" + resource_group_name = "existing" +} + +data "azurerm_elastic_san_volume_group" "example" { + name = "existing" + elastic_san_id = data.azurerm_elastic_san.example.id +} + +output "id" { + value = data.azurerm_elastic_san_volume_group.example.id +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `name` - The name of the Elastic SAN Volume Group. + +* `elastic_san_id` - The Elastic SAN ID within which the Elastic SAN Volume Group exists. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Elastic SAN. + +* `encryption_type` - The type of the key used to encrypt the data of the disk. + +* `encryption` - An `encryption` block as defined below. + +* `identity` - An `identity` block as defined below. + +* `network_rule` - One or more `network_rule` blocks as defined below. + +* `protocol_type` - The type of the storage target. + +--- + +An `encryption` block exports the following arguments: + +* `key_vault_key_id` - The Key Vault key URI for Customer Managed Key encryption, which can be either a full URI or a versionless URI. + +* `user_assigned_identity_id` - The ID of the User Assigned Identity used by this Elastic SAN Volume Group. + +* `current_versioned_key_expiration_timestamp` - The timestamp of the expiration time for the current version of the customer managed key. + +* `current_versioned_key_id` - The ID of the current versioned Key Vault Key in use. + +* `last_key_rotation_timestamp` - The timestamp of the last rotation of the Key Vault Key. + +--- + +An `identity` block exports the following arguments: + +* `type` - The type of Managed Identity assigned to this Elastic SAN Volume Group. + +* `identity_ids` - A list of the User Assigned Identity IDs assigned to this Elastic SAN Volume Group. + +* `principal_id` - The Principal ID associated with the Managed Service Identity assigned to this Elastic SAN Volume Group. + +* `tenant_id` - The Tenant ID associated with this Managed Service Identity assigned to this Elastic SAN Volume Group. + +--- + +A `network_rule` block exports the following arguments: + +* `subnet_id` - The ID of the Subnet which is allowed to access this Elastic SAN Volume Group. + +* `action` - The action to take when the Subnet attempts to access this Elastic SAN Volume Group. + +## 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 Elastic SAN Volume Group. From 300810fe74ab4cebb2a48bf6d2c160c1dd0e2029 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Wed, 29 May 2024 06:19:16 +0000 Subject: [PATCH 2/3] fix test --- .../elasticsan/elastic_san_volume_group_data_source_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/services/elasticsan/elastic_san_volume_group_data_source_test.go b/internal/services/elasticsan/elastic_san_volume_group_data_source_test.go index 62b4da1b5241..79592e983872 100644 --- a/internal/services/elasticsan/elastic_san_volume_group_data_source_test.go +++ b/internal/services/elasticsan/elastic_san_volume_group_data_source_test.go @@ -42,7 +42,7 @@ func (d ElasticSANVolumeGroupDataSource) basic(data acceptance.TestData) string %s data "azurerm_elastic_san_volume_group" "test" { - name = azurerm_elastic_san_volume_group.test.nameelastic_san_id + name = azurerm_elastic_san_volume_group.test.name elastic_san_id = azurerm_elastic_san_volume_group.test.elastic_san_id } `, ElasticSANVolumeGroupTestResource{}.complete(data)) From 1e0339d838fb3a339eae407b30245d296e1b6427 Mon Sep 17 00:00:00 2001 From: teowa <104055472+teowa@users.noreply.github.com> Date: Wed, 29 May 2024 08:34:41 +0000 Subject: [PATCH 3/3] set name & elastic_san_id earlier in read; fix words in doc --- .../elasticsan/elastic_san_volume_group_data_source.go | 10 +++++----- website/docs/d/elastic_san_volume_group.html.markdown | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/services/elasticsan/elastic_san_volume_group_data_source.go b/internal/services/elasticsan/elastic_san_volume_group_data_source.go index 63f05e48d8e8..9b22a9744ff5 100644 --- a/internal/services/elasticsan/elastic_san_volume_group_data_source.go +++ b/internal/services/elasticsan/elastic_san_volume_group_data_source.go @@ -138,13 +138,13 @@ func (r ElasticSANVolumeGroupDataSource) Read() sdk.ResourceFunc { return fmt.Errorf("retrieving %s: %+v", id, err) } - if model := resp.Model; model != nil { - state.SanId = elasticSanId.ID() - state.Name = id.VolumeGroupName + state.SanId = elasticSanId.ID() + state.Name = id.VolumeGroupName + if model := resp.Model; model != nil { flattenedIdentity, err := identity.FlattenSystemOrUserAssignedMapToModel(model.Identity) if err != nil { - return fmt.Errorf("flattening identity: %+v", err) + return fmt.Errorf("flattening `identity`: %+v", err) } state.Identity = *flattenedIdentity @@ -158,7 +158,7 @@ func (r ElasticSANVolumeGroupDataSource) Read() sdk.ResourceFunc { state.Encryption, err = FlattenVolumeGroupEncryption(model.Properties.EncryptionProperties) if err != nil { - return fmt.Errorf("flattening encryption: %+v", err) + return fmt.Errorf("flattening `encryption`: %+v", err) } } } diff --git a/website/docs/d/elastic_san_volume_group.html.markdown b/website/docs/d/elastic_san_volume_group.html.markdown index eac340c03e49..257e06e02297 100644 --- a/website/docs/d/elastic_san_volume_group.html.markdown +++ b/website/docs/d/elastic_san_volume_group.html.markdown @@ -56,11 +56,11 @@ In addition to the Arguments listed above - the following Attributes are exporte An `encryption` block exports the following arguments: -* `key_vault_key_id` - The Key Vault key URI for Customer Managed Key encryption, which can be either a full URI or a versionless URI. +* `key_vault_key_id` - The Key Vault Key URI for Customer Managed Key encryption, which can be either a full URI or a versionless URI. * `user_assigned_identity_id` - The ID of the User Assigned Identity used by this Elastic SAN Volume Group. -* `current_versioned_key_expiration_timestamp` - The timestamp of the expiration time for the current version of the customer managed key. +* `current_versioned_key_expiration_timestamp` - The timestamp of the expiration time for the current version of the Customer Managed Key. * `current_versioned_key_id` - The ID of the current versioned Key Vault Key in use. @@ -82,9 +82,9 @@ An `identity` block exports the following arguments: A `network_rule` block exports the following arguments: -* `subnet_id` - The ID of the Subnet which is allowed to access this Elastic SAN Volume Group. +* `subnet_id` - The ID of the Subnet from which access to this Elastic SAN Volume Group is allowed. -* `action` - The action to take when the Subnet attempts to access this Elastic SAN Volume Group. +* `action` - The action to take when an access attempt to this Elastic SAN Volume Group from this Subnet is made. ## Timeouts