diff --git a/internal/services/recoveryservices/recovery_services_vault_data_source.go b/internal/services/recoveryservices/recovery_services_vault_data_source.go index 87662390eca3..5b1156df20fb 100644 --- a/internal/services/recoveryservices/recovery_services_vault_data_source.go +++ b/internal/services/recoveryservices/recovery_services_vault_data_source.go @@ -4,78 +4,113 @@ package recoveryservices 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-helpers/resourcemanager/location" - "github.com/hashicorp/go-azure-helpers/resourcemanager/tags" "github.com/hashicorp/go-azure-sdk/resource-manager/recoveryservices/2024-01-01/vaults" - "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/recoveryservices/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" - "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" ) -func dataSourceRecoveryServicesVault() *pluginsdk.Resource { - return &pluginsdk.Resource{ - Read: dataSourceRecoveryServicesVaultRead, +var _ sdk.DataSource = SiteRecoveryRecoveryVaultDataSource{} - Timeouts: &pluginsdk.ResourceTimeout{ - Read: pluginsdk.DefaultTimeout(5 * time.Minute), - }, - - Schema: map[string]*pluginsdk.Schema{ - "name": { - Type: pluginsdk.TypeString, - Required: true, - }, - - "resource_group_name": commonschema.ResourceGroupNameForDataSource(), +type SiteRecoveryRecoveryVaultDataSource struct{} +type SiteRecoveryRecoveryVaultDataSourceModel struct { + Name string `tfschema:"name"` + ResourceGroupName string `tfschema:"resource_group_name"` + Location string `tfschema:"location"` + Identity []identity.ModelSystemAssignedUserAssigned `tfschema:"identity"` + Sku string `tfschema:"sku"` + Tags map[string]string `tfschema:"tags"` +} - "location": commonschema.LocationComputed(), +func (SiteRecoveryRecoveryVaultDataSource) ModelObject() interface{} { + return &SiteRecoveryRecoveryVaultDataSourceModel{} +} - "tags": commonschema.TagsDataSource(), +func (SiteRecoveryRecoveryVaultDataSource) ResourceType() string { + return "azurerm_recovery_services_vault" +} - "sku": { - Type: pluginsdk.TypeString, - Computed: true, - }, +func (SiteRecoveryRecoveryVaultDataSource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.RecoveryServicesVaultName, }, + + "resource_group_name": commonschema.ResourceGroupNameForDataSource(), } } +func (SiteRecoveryRecoveryVaultDataSource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "location": commonschema.LocationComputed(), -func dataSourceRecoveryServicesVaultRead(d *pluginsdk.ResourceData, meta interface{}) error { - client := meta.(*clients.Client).RecoveryServices.VaultsClient - subscriptionId := meta.(*clients.Client).Account.SubscriptionId - ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) - defer cancel() + "tags": commonschema.TagsDataSource(), - id := vaults.NewVaultID(subscriptionId, d.Get("resource_group_name").(string), d.Get("name").(string)) - resp, err := client.Get(ctx, id) - if err != nil { - if response.WasNotFound(resp.HttpResponse) { - return fmt.Errorf("%s was not found", id) - } + "identity": commonschema.SystemAssignedUserAssignedIdentityComputed(), - return fmt.Errorf("retrieving %s: %+v", id, err) - } - - if resp.Model == nil { - return fmt.Errorf("retrieving %s: `model` was nil", id) + "sku": { + Type: pluginsdk.TypeString, + Computed: true, + }, } - model := resp.Model - - d.SetId(id.ID()) - d.Set("name", id.VaultName) - d.Set("resource_group_name", id.ResourceGroupName) - d.Set("location", location.Normalize(model.Location)) +} - skuName := "" - if model.Sku != nil { - skuName = string(model.Sku.Name) +func (r SiteRecoveryRecoveryVaultDataSource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.RecoveryServices.VaultsClient + subscriptionId := metadata.Client.Account.SubscriptionId + + var recoveryServiceVault SiteRecoveryRecoveryVaultDataSourceModel + if err := metadata.Decode(&recoveryServiceVault); err != nil { + return err + } + + id := vaults.NewVaultID(subscriptionId, recoveryServiceVault.ResourceGroupName, recoveryServiceVault.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 { + flattenedIdentity, err := identity.FlattenSystemAndUserAssignedMapToModel(model.Identity) + + if err != nil { + return fmt.Errorf("flattening `identity`: %+v", err) + } + + skuName := "" + if model.Sku != nil { + skuName = string(model.Sku.Name) + } + + recoveryServiceVault.Sku = skuName + recoveryServiceVault.Location = location.Normalize(model.Location) + recoveryServiceVault.Tags = pointer.From(model.Tags) + recoveryServiceVault.Identity = pointer.From(flattenedIdentity) + } + + metadata.SetID(id) + + if err := metadata.Encode(&recoveryServiceVault); err != nil { + return fmt.Errorf("encoding: %+v", err) + } + + return nil + }, } - d.Set("sku", skuName) - - return tags.FlattenAndSet(d, model.Tags) } diff --git a/internal/services/recoveryservices/recovery_services_vault_data_source_test.go b/internal/services/recoveryservices/recovery_services_vault_data_source_test.go index 492d4fa9c7c3..ce41b5bb3c38 100644 --- a/internal/services/recoveryservices/recovery_services_vault_data_source_test.go +++ b/internal/services/recoveryservices/recovery_services_vault_data_source_test.go @@ -26,6 +26,28 @@ func TestAccDataSourceAzureRMRecoveryServicesVault_basic(t *testing.T) { check.That(data.ResourceName).Key("resource_group_name").Exists(), check.That(data.ResourceName).Key("tags.%").HasValue("0"), check.That(data.ResourceName).Key("sku").HasValue("Standard"), + check.That(data.ResourceName).Key("identity.%").HasValue("0"), + ), + }, + }) +} + +func TestAccDataSourceAzureRMRecoveryServicesVault_basicWithIdentity(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_recovery_services_vault", "test") + r := RecoveryServicesVaultDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basicWithIdentity(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("location").Exists(), + check.That(data.ResourceName).Key("resource_group_name").Exists(), + check.That(data.ResourceName).Key("tags.%").HasValue("0"), + check.That(data.ResourceName).Key("sku").HasValue("Standard"), + check.That(data.ResourceName).Key("identity.0.type").HasValue("SystemAssigned"), + check.That(data.ResourceName).Key("identity.0.tenant_id").IsNotEmpty(), + check.That(data.ResourceName).Key("identity.0.principal_id").IsNotEmpty(), ), }, }) @@ -41,3 +63,14 @@ data "azurerm_recovery_services_vault" "test" { } `, RecoveryServicesVaultResource{}.basic(data)) } + +func (RecoveryServicesVaultDataSource) basicWithIdentity(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_recovery_services_vault" "test" { + name = azurerm_recovery_services_vault.test.name + resource_group_name = azurerm_resource_group.test.name +} +`, RecoveryServicesVaultResource{}.basicWithIdentity(data)) +} diff --git a/internal/services/recoveryservices/registration.go b/internal/services/recoveryservices/registration.go index 27bff567db10..e073c97110fa 100644 --- a/internal/services/recoveryservices/registration.go +++ b/internal/services/recoveryservices/registration.go @@ -21,6 +21,7 @@ func (r Registration) AssociatedGitHubLabel() string { func (r Registration) DataSources() []sdk.DataSource { return []sdk.DataSource{ + SiteRecoveryRecoveryVaultDataSource{}, SiteRecoveryReplicationRecoveryPlanDataSource{}, } } @@ -55,7 +56,6 @@ func (r Registration) WebsiteCategories() []string { // SupportedDataSources returns the supported Data Sources supported by this Service func (r Registration) SupportedDataSources() map[string]*pluginsdk.Resource { return map[string]*pluginsdk.Resource{ - "azurerm_recovery_services_vault": dataSourceRecoveryServicesVault(), "azurerm_site_recovery_fabric": dataSourceSiteRecoveryFabric(), "azurerm_site_recovery_protection_container": dataSourceSiteRecoveryProtectionContainer(), "azurerm_backup_policy_vm": dataSourceBackupPolicyVm(), diff --git a/website/docs/d/recovery_services_vault.html.markdown b/website/docs/d/recovery_services_vault.html.markdown index e47ab1679e49..8310ff51a7cd 100644 --- a/website/docs/d/recovery_services_vault.html.markdown +++ b/website/docs/d/recovery_services_vault.html.markdown @@ -39,6 +39,18 @@ The following attributes are exported: * `sku` - The vault's current SKU. +* `identity` - (Optional) An `identity` block as defined below. + +--- + +An `identity` block exports the following: + +* `principal_id` - The Principal ID associated with this Managed Service Identity. + +* `tenant_id` - The Tenant ID associated with this Managed Service Identity. + +* `type` - The identity type of this Managed Service Identity. + ## Timeouts The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: