diff --git a/internal/services/keyvault/key_vault_managed_storage_account.go b/internal/services/keyvault/key_vault_managed_storage_account.go new file mode 100644 index 000000000000..7b040925be97 --- /dev/null +++ b/internal/services/keyvault/key_vault_managed_storage_account.go @@ -0,0 +1,282 @@ +package keyvault + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/parse" + keyVaultParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/parse" + keyVaultValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" + storageValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tags" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceKeyVaultManagedStorageAccount() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceKeyVaultManagedStorageAccountCreateUpdate, + Read: resourceKeyVaultManagedStorageAccountRead, + Update: resourceKeyVaultManagedStorageAccountCreateUpdate, + Delete: resourceKeyVaultManagedStorageAccountDelete, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := keyVaultParse.ParseOptionallyVersionedNestedItemID(id) + return err + }), + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(30 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: keyVaultValidate.NestedItemName, + }, + + "key_vault_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: keyVaultValidate.VaultID, + }, + + "storage_account_key": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "key1", + "key2", + }, false), + }, + + "storage_account_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: storageValidate.StorageAccountID, + }, + + "regenerate_key_automatically": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + RequiredWith: []string{"regeneration_period"}, + }, + + "regeneration_period": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validate.ISO8601Duration, + RequiredWith: []string{"regenerate_key_automatically"}, + }, + + "tags": tags.ForceNewSchema(), + }, + } +} + +func resourceKeyVaultManagedStorageAccountCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + keyVaultsClient := meta.(*clients.Client).KeyVault + client := meta.(*clients.Client).KeyVault.ManagementClient + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + defer cancel() + + name := d.Get("name").(string) + keyVaultId, err := parse.VaultID(d.Get("key_vault_id").(string)) + if err != nil { + return err + } + + keyVaultBaseUrl, err := keyVaultsClient.BaseUriForKeyVault(ctx, *keyVaultId) + if err != nil { + return fmt.Errorf("looking up Base URI for Managed Storage Account Key Vault %q in %s: %+v", name, *keyVaultId, err) + } + + if d.IsNewResource() { + existing, err := client.GetStorageAccount(ctx, *keyVaultBaseUrl, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for presence of existing Managed Storage Account %q (Key Vault %q): %s", name, *keyVaultId, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_key_vault_managed_storage_account", *existing.ID) + } + } + + t := d.Get("tags").(map[string]interface{}) + + parameters := keyvault.StorageAccountCreateParameters{ + ResourceID: utils.String(d.Get("storage_account_id").(string)), + ActiveKeyName: utils.String(d.Get("storage_account_key").(string)), + AutoRegenerateKey: utils.Bool(d.Get("regenerate_key_automatically").(bool)), + RegenerationPeriod: utils.String(d.Get("regeneration_period").(string)), + Tags: tags.Expand(t), + } + + if resp, err := client.SetStorageAccount(ctx, *keyVaultBaseUrl, name, parameters); err != nil { + // In the case that the Storage Account already exists in a Soft Deleted / Recoverable state we check if `recover_soft_deleted_key_vaults` is set + // and attempt recovery where appropriate + if meta.(*clients.Client).Features.KeyVault.RecoverSoftDeletedKeyVaults && utils.ResponseWasConflict(resp.Response) { + recoveredStorageAccount, err := client.RecoverDeletedStorageAccount(ctx, *keyVaultBaseUrl, name) + if err != nil { + return fmt.Errorf("recovery of Managed Storage Account %q (Key Vault %q): %s", name, *keyVaultBaseUrl, err) + } + log.Printf("[DEBUG] Recovering Managed Storage Account %q (Key Vault %q)", name, *keyVaultBaseUrl) + // We need to wait for consistency, recovered Key Vault Child items are not as readily available as newly created + if secret := recoveredStorageAccount.ID; secret != nil { + stateConf := &pluginsdk.StateChangeConf{ + Pending: []string{"pending"}, + Target: []string{"available"}, + Refresh: keyVaultChildItemRefreshFunc(*secret), + Delay: 30 * time.Second, + PollInterval: 10 * time.Second, + ContinuousTargetOccurence: 10, + Timeout: d.Timeout(pluginsdk.TimeoutCreate), + } + + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for Managed Storage Account %q (Key Vault %q) to become available after recovery: %s", name, *keyVaultId, err) + } + log.Printf("[DEBUG] Managed Storage Account %q recovered with ID: %q", name, *recoveredStorageAccount.ID) + + if _, err := client.SetStorageAccount(ctx, *keyVaultBaseUrl, name, parameters); err != nil { + return fmt.Errorf("creating Managed Storage Account %s (Key Vault %q): %+v", name, *keyVaultId, err) + } + } + } else { + return fmt.Errorf("creating Managed Storage Account %s: %+v", name, err) + } + } + + read, err := client.GetStorageAccount(ctx, *keyVaultBaseUrl, name) + if err != nil { + return fmt.Errorf("cannot read Managed Storage Account %q (Key Vault %q): %+v", name, *keyVaultId, err) + } + if read.ID == nil { + return fmt.Errorf("cannot read Managed Storage Account ID %q (Key Vault %q)", name, *keyVaultId) + } + + d.SetId(*read.ID) + + return resourceKeyVaultManagedStorageAccountRead(d, meta) +} + +func resourceKeyVaultManagedStorageAccountRead(d *pluginsdk.ResourceData, meta interface{}) error { + keyVaultsClient := meta.(*clients.Client).KeyVault + client := meta.(*clients.Client).KeyVault.ManagementClient + resourcesClient := meta.(*clients.Client).Resource + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.ParseOptionallyVersionedNestedItemID(d.Id()) + if err != nil { + return err + } + + keyVaultIdRaw, err := keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, resourcesClient, id.KeyVaultBaseUrl) + if err != nil { + return fmt.Errorf("retrieving the Resource ID of the Key Vault at URL %q: %s", id.KeyVaultBaseUrl, err) + } + if keyVaultIdRaw == nil { + log.Printf("[DEBUG] Unable to determine the Resource ID for the Key Vault at URL %q - removing from state!", id.KeyVaultBaseUrl) + d.SetId("") + return nil + } + + keyVaultId, err := parse.VaultID(*keyVaultIdRaw) + if err != nil { + return err + } + + ok, err := keyVaultsClient.Exists(ctx, *keyVaultId) + if err != nil { + return fmt.Errorf("checking if Key Vault %q for Managed Storage Account %q exists: %v", *keyVaultId, id.Name, err) + } + if !ok { + log.Printf("[DEBUG] Managed Storage Account %q (Key Vault %q) was not found - removing from state", id.Name, *keyVaultId) + d.SetId("") + return nil + } + + resp, err := client.GetStorageAccount(ctx, id.KeyVaultBaseUrl, id.Name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] Managed Storage Account %q (Key Vault %q) was not found - removing from state", id.Name, *keyVaultId) + d.SetId("") + return nil + } + return fmt.Errorf("Error making Read request on Managed Storage Account %q (Key Vault %q): %+v", id.Name, *keyVaultId, err) + } + + d.Set("name", id.Name) + d.Set("key_vault_id", keyVaultId.ID()) + d.Set("storage_account_id", resp.ResourceID) + d.Set("storage_account_key", resp.ActiveKeyName) + d.Set("regenerate_key_automatically", resp.AutoRegenerateKey) + d.Set("regeneration_period", resp.RegenerationPeriod) + + return tags.FlattenAndSet(d, resp.Tags) +} + +func resourceKeyVaultManagedStorageAccountDelete(d *pluginsdk.ResourceData, meta interface{}) error { + keyVaultsClient := meta.(*clients.Client).KeyVault + client := meta.(*clients.Client).KeyVault.ManagementClient + resourcesClient := meta.(*clients.Client).Resource + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.ParseOptionallyVersionedNestedItemID(d.Id()) + if err != nil { + return err + } + + keyVaultIdRaw, err := keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, resourcesClient, id.KeyVaultBaseUrl) + if err != nil { + return fmt.Errorf("retrieving the Resource ID the Key Vault at URL %q: %s", id.KeyVaultBaseUrl, err) + } + if keyVaultIdRaw == nil { + return fmt.Errorf("determining the Resource ID for the Key Vault at URL %q", id.KeyVaultBaseUrl) + } + keyVaultId, err := parse.VaultID(*keyVaultIdRaw) + if err != nil { + return err + } + + ok, err := keyVaultsClient.Exists(ctx, *keyVaultId) + if err != nil { + return fmt.Errorf("checking for existence of Key Vault %q for Managed Storage Account %q in Vault at url %q: %v", *keyVaultId, id.Name, id.KeyVaultBaseUrl, err) + } + if !ok { + log.Printf("[DEBUG] Managed Storage Account %q (Key Vault %q) was not found in Key Vault at URI %q - removing from state", id.Name, *keyVaultId, id.KeyVaultBaseUrl) + d.SetId("") + return nil + } + + shouldPurge := meta.(*clients.Client).Features.KeyVault.PurgeSoftDeleteOnDestroy + description := fmt.Sprintf("Secret %q (Key Vault %q)", id.Name, id.KeyVaultBaseUrl) + deleter := deleteAndPurgeSecret{ + client: client, + keyVaultUri: id.KeyVaultBaseUrl, + name: id.Name, + } + if err := deleteAndOptionallyPurge(ctx, description, shouldPurge, deleter); err != nil { + return err + } + + return nil +} diff --git a/internal/services/keyvault/key_vault_managed_storage_account_sas_token_definition.go b/internal/services/keyvault/key_vault_managed_storage_account_sas_token_definition.go new file mode 100644 index 000000000000..847675aedf71 --- /dev/null +++ b/internal/services/keyvault/key_vault_managed_storage_account_sas_token_definition.go @@ -0,0 +1,272 @@ +package keyvault + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.1/keyvault" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/parse" + keyVaultValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tags" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceKeyVaultManagedStorageAccountSasTokenDefinition() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceKeyVaultManagedStorageAccountSasTokenDefinitionCreateUpdate, + Read: resourceKeyVaultManagedStorageAccountSasTokenDefinitionRead, + Update: resourceKeyVaultManagedStorageAccountSasTokenDefinitionCreateUpdate, + Delete: resourceKeyVaultManagedStorageAccountSasTokenDefinitionDelete, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.SasDefinitionID(id) + return err + }), + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(30 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: keyVaultValidate.NestedItemName, + }, + + "managed_storage_account_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: keyVaultValidate.VersionlessNestedItemId, + }, + + "sas_template_uri": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "sas_type": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "service", + "account", + }, false), + }, + + "secret_id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "validity_period": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.ISO8601Duration, + }, + + "tags": tags.ForceNewSchema(), + }, + } +} + +func resourceKeyVaultManagedStorageAccountSasTokenDefinitionCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + keyVaultsClient := meta.(*clients.Client).KeyVault + client := meta.(*clients.Client).KeyVault.ManagementClient + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + resourcesClient := meta.(*clients.Client).Resource + defer cancel() + + name := d.Get("name").(string) + storageAccount, err := parse.ParseOptionallyVersionedNestedItemID(d.Get("managed_storage_account_id").(string)) + if err != nil { + return err + } + + keyVaultIdRaw, err := keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, resourcesClient, storageAccount.KeyVaultBaseUrl) + if err != nil { + return fmt.Errorf("retrieving the Resource ID of the Key Vault at URL %q: %s", storageAccount.KeyVaultBaseUrl, err) + } + keyVaultId, err := parse.VaultID(*keyVaultIdRaw) + if err != nil { + return err + } + + keyVaultBaseUri, err := keyVaultsClient.BaseUriForKeyVault(ctx, *keyVaultId) + if err != nil { + return fmt.Errorf("looking up Base URI for Managed Storage Account Key Vault %s: %+v", *keyVaultId, err) + } + + if d.IsNewResource() { + existing, err := client.GetSasDefinition(ctx, *keyVaultBaseUri, storageAccount.Name, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for presence of existing Managed Storage Account Sas Defition %q (Storage Account %q, Key Vault %q): %+v", name, storageAccount.Name, *keyVaultId, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_key_vault_managed_storage_account_sas_token_definition", *existing.ID) + } + } + + t := d.Get("tags").(map[string]interface{}) + parameters := keyvault.SasDefinitionCreateParameters{ + TemplateURI: utils.String(d.Get("sas_template_uri").(string)), + SasType: keyvault.SasTokenType(d.Get("sas_type").(string)), + ValidityPeriod: utils.String(d.Get("validity_period").(string)), + SasDefinitionAttributes: &keyvault.SasDefinitionAttributes{ + Enabled: utils.Bool(true), + }, + Tags: tags.Expand(t), + } + + if resp, err := client.SetSasDefinition(ctx, *keyVaultBaseUri, storageAccount.Name, name, parameters); err != nil { + // In the case that the Storage Account already exists in a Soft Deleted / Recoverable state we check if `recover_soft_deleted_key_vaults` is set + // and attempt recovery where appropriate + if meta.(*clients.Client).Features.KeyVault.RecoverSoftDeletedKeyVaults && utils.ResponseWasConflict(resp.Response) { + recoveredStorageAccount, err := client.RecoverDeletedSasDefinition(ctx, *keyVaultBaseUri, storageAccount.Name, name) + if err != nil { + return fmt.Errorf("recovery of Managed Storage Account SAS Definition %q (Storage Account %q, Key Vault %q): %+v", name, storageAccount.Name, *keyVaultId, err) + } + log.Printf("[DEBUG] Recovering Managed Storage Account Sas Definition %q (Storage Account %q, Key Vault %q)", name, storageAccount.Name, *keyVaultId) + // We need to wait for consistency, recovered Key Vault Child items are not as readily available as newly created + if secret := recoveredStorageAccount.ID; secret != nil { + stateConf := &pluginsdk.StateChangeConf{ + Pending: []string{"pending"}, + Target: []string{"available"}, + Refresh: keyVaultChildItemRefreshFunc(*secret), + Delay: 30 * time.Second, + PollInterval: 10 * time.Second, + ContinuousTargetOccurence: 10, + Timeout: d.Timeout(pluginsdk.TimeoutCreate), + } + + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for Key Vault Managed Storage Account Sas Definition %q (Storage Account %q, Key Vault %q) to become available: %s", name, storageAccount.Name, *keyVaultId, err) + } + log.Printf("[DEBUG] Managed Storage Account Sas Definition %q (Storage Account %q, Key Vault %q) recovered", name, storageAccount.Name, *keyVaultId) + + if _, err := client.SetSasDefinition(ctx, *keyVaultBaseUri, storageAccount.Name, name, parameters); err != nil { + return fmt.Errorf("creation of Managed Storage Account SAS Definition %q (Storage Account %q, Key Vault %q): %+v", name, storageAccount.Name, *keyVaultId, err) + } + } + } else { + return fmt.Errorf("creation of Managed Storage Account SAS Definition %q (Storage Account %q, Key Vault %q): %+v", name, storageAccount.Name, *keyVaultId, err) + } + } + + read, err := client.GetSasDefinition(ctx, *keyVaultBaseUri, storageAccount.Name, name) + + if err != nil { + return err + } + + if read.ID == nil { + return fmt.Errorf("cannot read Managed Storage Account Sas Definition %q (Storage Account %q, Key Vault %q)", name, storageAccount.Name, *keyVaultId) + } + + d.SetId(*read.ID) + + return resourceKeyVaultManagedStorageAccountSasTokenDefinitionRead(d, meta) +} + +func resourceKeyVaultManagedStorageAccountSasTokenDefinitionRead(d *pluginsdk.ResourceData, meta interface{}) error { + keyVaultsClient := meta.(*clients.Client).KeyVault + client := meta.(*clients.Client).KeyVault.ManagementClient + resourcesClient := meta.(*clients.Client).Resource + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.SasDefinitionID(d.Id()) + if err != nil { + return err + } + + keyVaultIdRaw, err := keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, resourcesClient, id.KeyVaultBaseUrl) + if err != nil { + return fmt.Errorf("retrieving the Resource ID of the Key Vault at URL %q: %s", id.KeyVaultBaseUrl, err) + } + + keyVaultId, err := parse.VaultID(*keyVaultIdRaw) + if err != nil { + return err + } + + ok, err := keyVaultsClient.Exists(ctx, *keyVaultId) + if err != nil { + return fmt.Errorf("checking for presence of existing Managed Storage Account Sas Defition %q (Key Vault %q): %+v", id.Name, id.KeyVaultBaseUrl, err) + } + if !ok { + log.Printf("[DEBUG] Managed Storage Account Sas Definition %q Key Vault %q was not found in Key Vault at URI %q - removing from state", id.Name, *keyVaultId, id.KeyVaultBaseUrl) + d.SetId("") + return nil + } + + resp, err := client.GetSasDefinition(ctx, id.KeyVaultBaseUrl, id.StorageAccountName, id.Name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] Managed Storage Account Sas Definition %q was not found in Key Vault at URI %q - removing from state", id.Name, id.KeyVaultBaseUrl) + d.SetId("") + return nil + } + return fmt.Errorf("cannot read Managed Storage Account Sas Definition %s: %+v", id.Name, err) + } + + d.Set("name", id.Name) + d.Set("sas_template_uri", resp.TemplateURI) + d.Set("sas_type", resp.SasType) + d.Set("secret_id", resp.SecretID) + d.Set("validity_period", resp.ValidityPeriod) + + return tags.FlattenAndSet(d, resp.Tags) +} + +func resourceKeyVaultManagedStorageAccountSasTokenDefinitionDelete(d *pluginsdk.ResourceData, meta interface{}) error { + keyVaultsClient := meta.(*clients.Client).KeyVault + client := meta.(*clients.Client).KeyVault.ManagementClient + resourcesClient := meta.(*clients.Client).Resource + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.SasDefinitionID(d.Id()) + if err != nil { + return err + } + + keyVaultIdRaw, err := keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, resourcesClient, id.KeyVaultBaseUrl) + if err != nil { + return fmt.Errorf("retrieving the Resource ID of the Key Vault at URL %q: %s", id.KeyVaultBaseUrl, err) + } + if keyVaultIdRaw == nil { + return fmt.Errorf("unable to determine the Resource ID for the Key Vault at URL %q", id.KeyVaultBaseUrl) + } + keyVaultId, err := parse.VaultID(*keyVaultIdRaw) + if err != nil { + return err + } + + ok, err := keyVaultsClient.Exists(ctx, *keyVaultId) + if err != nil { + return fmt.Errorf("checking if key vault %q for Managed Storage Account Sas Definition %q in Vault at url %q exists: %v", *keyVaultId, id.Name, id.KeyVaultBaseUrl, err) + } + if !ok { + log.Printf("[DEBUG] Managed Storage Account Sas Definition %q Key Vault %q was not found in Key Vault at URI %q - removing from state", id.Name, *keyVaultId, id.KeyVaultBaseUrl) + d.SetId("") + return nil + } + + _, err = client.DeleteSasDefinition(ctx, id.KeyVaultBaseUrl, id.StorageAccountName, id.Name) + return err +} diff --git a/internal/services/keyvault/key_vault_managed_storage_account_sas_token_definition_test.go b/internal/services/keyvault/key_vault_managed_storage_account_sas_token_definition_test.go new file mode 100644 index 000000000000..c2a6a4b30cca --- /dev/null +++ b/internal/services/keyvault/key_vault_managed_storage_account_sas_token_definition_test.go @@ -0,0 +1,342 @@ +package keyvault_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type KeyVaultManagedStorageAccountSasTokenDefinitionResource struct { +} + +func TestAccKeyVaultManagedStorageAccountSasTokenDefinition_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_key_vault_managed_storage_account_sas_token_definition", "test") + r := KeyVaultManagedStorageAccountSasTokenDefinitionResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("managed_storage_account_id"), + }) +} + +func TestAccKeyVaultManagedStorageAccountSasTokenDefinition_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_key_vault_managed_storage_account_sas_token_definition", "test") + r := KeyVaultManagedStorageAccountSasTokenDefinitionResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccKeyVaultManagedStorageAccountSasTokenDefinition_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_key_vault_managed_storage_account_sas_token_definition", "test") + r := KeyVaultManagedStorageAccountSasTokenDefinitionResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data, "P1D"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sas_type").HasValue("account"), + check.That(data.ResourceName).Key("validity_period").HasValue("P1D"), + check.That(data.ResourceName).Key("secret_id").HasValue(fmt.Sprintf("https://acctestkv-%s.vault.azure.net/secrets/acctestKVstorage-acctestKVsasdefinition", data.RandomString)), + check.That(data.ResourceName).Key("tags.%").HasValue("1"), + check.That(data.ResourceName).Key("tags.hello").HasValue("world"), + ), + }, + data.ImportStep("managed_storage_account_id"), + }) +} + +func TestAccKeyVaultManagedStorageAccountSasTokenDefinition_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_key_vault_managed_storage_account_sas_token_definition", "test") + r := KeyVaultManagedStorageAccountSasTokenDefinitionResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data, "P1D"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sas_type").HasValue("account"), + check.That(data.ResourceName).Key("validity_period").HasValue("P1D"), + check.That(data.ResourceName).Key("secret_id").HasValue(fmt.Sprintf("https://acctestkv-%s.vault.azure.net/secrets/acctestKVstorage-acctestKVsasdefinition", data.RandomString)), + check.That(data.ResourceName).Key("tags.%").HasValue("1"), + check.That(data.ResourceName).Key("tags.hello").HasValue("world"), + ), + }, + data.ImportStep("managed_storage_account_id"), + { + Config: r.complete(data, "P2D"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("sas_type").HasValue("account"), + check.That(data.ResourceName).Key("validity_period").HasValue("P2D"), + check.That(data.ResourceName).Key("secret_id").HasValue(fmt.Sprintf("https://acctestkv-%s.vault.azure.net/secrets/acctestKVstorage-acctestKVsasdefinition", data.RandomString)), + check.That(data.ResourceName).Key("tags.%").HasValue("1"), + check.That(data.ResourceName).Key("tags.hello").HasValue("world"), + ), + }, + data.ImportStep("managed_storage_account_id"), + }) +} + +func TestAccKeyVaultManagedStorageAccountSasTokenDefinition_recovery(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_key_vault_managed_storage_account_sas_token_definition", "test") + r := KeyVaultManagedStorageAccountSasTokenDefinitionResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.softDeleteRecovery(data, false, "1"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("managed_storage_account_id"), + { + Config: r.softDeleteRecovery(data, false, "1"), + Destroy: true, + }, + { + // purge true here to make sure when we end the test there's no soft-deleted items left behind + Config: r.softDeleteRecovery(data, true, "2"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("managed_storage_account_id"), + }) +} + +func (r KeyVaultManagedStorageAccountSasTokenDefinitionResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_key_vault_managed_storage_account" "test" { + name = "acctestKVstorage" + key_vault_id = azurerm_key_vault.test.id + storage_account_id = azurerm_storage_account.test.id + storage_account_key = "key1" + regenerate_key_automatically = false + regeneration_period = "P1D" +} + +resource "azurerm_key_vault_managed_storage_account_sas_token_definition" "test" { + name = "acctestKVsasdefinition" + managed_storage_account_id = azurerm_key_vault_managed_storage_account.test.id + sas_type = "account" + sas_template_uri = data.azurerm_storage_account_sas.test.sas + validity_period = "P1D" +} +`, r.template(data)) +} + +func (r KeyVaultManagedStorageAccountSasTokenDefinitionResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_key_vault_managed_storage_account_sas_token_definition" "import" { + name = azurerm_key_vault_managed_storage_account_sas_token_definition.test.name + managed_storage_account_id = azurerm_key_vault_managed_storage_account.test.id + sas_type = "account" + sas_template_uri = data.azurerm_storage_account_sas.test.sas + validity_period = "P1D" +} +`, r.basic(data)) +} + +func (r KeyVaultManagedStorageAccountSasTokenDefinitionResource) complete(data acceptance.TestData, validyPeriod string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_key_vault_managed_storage_account" "test" { + name = "acctestKVstorage" + key_vault_id = azurerm_key_vault.test.id + storage_account_id = azurerm_storage_account.test.id + storage_account_key = "key1" + regenerate_key_automatically = false + regeneration_period = "P1D" +} + +resource "azurerm_key_vault_managed_storage_account_sas_token_definition" "test" { + name = "acctestKVsasdefinition" + managed_storage_account_id = azurerm_key_vault_managed_storage_account.test.id + sas_type = "account" + sas_template_uri = data.azurerm_storage_account_sas.test.sas + validity_period = "%s" + + tags = { + "hello" = "world" + } +} +`, r.template(data), validyPeriod) +} + +func (r KeyVaultManagedStorageAccountSasTokenDefinitionResource) softDeleteRecovery(data acceptance.TestData, purge bool, name string) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + key_vault { + purge_soft_delete_on_destroy = "%t" + recover_soft_deleted_key_vaults = true + } + } +} + +%s + +resource "azurerm_key_vault_managed_storage_account" "test" { + name = "acctestKVstorage%s" + key_vault_id = azurerm_key_vault.test.id + storage_account_id = azurerm_storage_account.test.id + storage_account_key = "key1" + regenerate_key_automatically = false + regeneration_period = "P1D" +} + +resource "azurerm_key_vault_managed_storage_account_sas_token_definition" "test" { + name = "acctestKVsasdefinition%s" + managed_storage_account_id = azurerm_key_vault_managed_storage_account.test.id + sas_type = "account" + sas_template_uri = data.azurerm_storage_account_sas.test.sas + validity_period = "P1D" +} +`, purge, r.template(data), name, name) +} + +func (KeyVaultManagedStorageAccountSasTokenDefinitionResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctest-kv-RG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "unlikely23exst2acct%s" + resource_group_name = azurerm_resource_group.test.name + + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +data "azurerm_storage_account_sas" "test" { + connection_string = azurerm_storage_account.test.primary_connection_string + https_only = true + + resource_types { + service = true + container = false + object = false + } + + services { + blob = true + queue = false + table = false + file = false + } + + start = "2021-04-30T00:00:00Z" + expiry = "2023-04-30T00:00:00Z" + + permissions { + read = true + write = true + delete = false + list = false + add = true + create = true + update = false + process = false + } +} + +resource "azurerm_key_vault" "test" { + name = "acctestkv-%s" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" + + access_policy { + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + + secret_permissions = [ + "Get", + "Delete" + ] + + storage_permissions = [ + "Get", + "List", + "Set", + "SetSAS", + "GetSAS", + "DeleteSAS", + "Update", + "RegenerateKey" + ] + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomString) +} + +func (KeyVaultManagedStorageAccountSasTokenDefinitionResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + dataPlaneClient := client.KeyVault.ManagementClient + keyVaultsClient := client.KeyVault + + id, err := parse.SasDefinitionID(state.ID) + if err != nil { + return nil, err + } + + keyVaultIdRaw, err := keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, client.Resource, id.KeyVaultBaseUrl) + if err != nil || keyVaultIdRaw == nil { + return nil, fmt.Errorf("retrieving the Resource ID the Key Vault at URL %q: %s", id.KeyVaultBaseUrl, err) + } + keyVaultId, err := parse.VaultID(*keyVaultIdRaw) + if err != nil { + return nil, err + } + + ok, err := keyVaultsClient.Exists(ctx, *keyVaultId) + if err != nil || !ok { + return nil, fmt.Errorf("checking if key vault %q for Managed Storage Account Sas Definition %q in Vault at url %q exists: %v", *keyVaultId, id.Name, id.KeyVaultBaseUrl, err) + } + + resp, err := dataPlaneClient.GetSasDefinition(ctx, id.KeyVaultBaseUrl, id.StorageAccountName, id.Name) + if err != nil { + return nil, fmt.Errorf("retrieving Key Vault Managed Storage Account Sas Definition %q: %+v", state.ID, err) + } + + return utils.Bool(resp.ID != nil), nil +} diff --git a/internal/services/keyvault/key_vault_managed_storage_account_test.go b/internal/services/keyvault/key_vault_managed_storage_account_test.go new file mode 100644 index 000000000000..b741eabdf951 --- /dev/null +++ b/internal/services/keyvault/key_vault_managed_storage_account_test.go @@ -0,0 +1,297 @@ +package keyvault_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type KeyVaultManagedStorageAccountResource struct { +} + +func TestAccKeyVaultManagedStorageAccount_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_key_vault_managed_storage_account", "test") + r := KeyVaultManagedStorageAccountResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccKeyVaultManagedStorageAccount_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_key_vault_managed_storage_account", "test") + r := KeyVaultManagedStorageAccountResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccKeyVaultManagedStorageAccount_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_key_vault_managed_storage_account", "test") + r := KeyVaultManagedStorageAccountResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data, true, "P1D"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("storage_account_key").HasValue("key1"), + check.That(data.ResourceName).Key("regenerate_key_automatically").HasValue("true"), + check.That(data.ResourceName).Key("regeneration_period").HasValue("P1D"), + check.That(data.ResourceName).Key("tags.%").HasValue("1"), + check.That(data.ResourceName).Key("tags.hello").HasValue("world"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccKeyVaultManagedStorageAccount_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_key_vault_managed_storage_account", "test") + r := KeyVaultManagedStorageAccountResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data, true, "P1D"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("storage_account_key").HasValue("key1"), + check.That(data.ResourceName).Key("regenerate_key_automatically").HasValue("true"), + check.That(data.ResourceName).Key("regeneration_period").HasValue("P1D"), + ), + }, + data.ImportStep(), + { + Config: r.complete(data, false, "P2D"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("storage_account_key").HasValue("key1"), + check.That(data.ResourceName).Key("regenerate_key_automatically").HasValue("false"), + check.That(data.ResourceName).Key("regeneration_period").HasValue("P2D"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccKeyVaultManagedStorageAccount_recovery(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_key_vault_managed_storage_account", "test") + r := KeyVaultManagedStorageAccountResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.softDeleteRecovery(data, false, "1"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.softDeleteRecovery(data, false, "1"), + Destroy: true, + }, + { + // purge true here to make sure when we end the test there's no soft-deleted items left behind + Config: r.softDeleteRecovery(data, true, "2"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r KeyVaultManagedStorageAccountResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +resource "azurerm_key_vault_managed_storage_account" "test" { + name = "acctestKVstorage" + key_vault_id = azurerm_key_vault.test.id + storage_account_id = azurerm_storage_account.test.id + storage_account_key = "key1" + regenerate_key_automatically = false + regeneration_period = "P1D" +} +`, r.template(data)) +} + +func (r KeyVaultManagedStorageAccountResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_key_vault_managed_storage_account" "import" { + name = azurerm_key_vault_managed_storage_account.test.name + key_vault_id = azurerm_key_vault_managed_storage_account.test.key_vault_id + storage_account_id = azurerm_key_vault_managed_storage_account.test.storage_account_id + storage_account_key = "key2" + regenerate_key_automatically = false + regeneration_period = "P1D" +} +`, r.basic(data)) +} + +func (r KeyVaultManagedStorageAccountResource) complete(data acceptance.TestData, autoGenerateKey bool, regenPeriod string) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +%s + +provider "azuread" {} + +data "azuread_service_principal" "test" { + # https://docs.microsoft.com/en-us/azure/key-vault/secrets/overview-storage-keys-powershell#service-principal-application-id + # application_id = "cfa8b339-82a2-471a-a3c9-0fc0be7a4093" + display_name = "Azure Key Vault" +} + +resource "azurerm_role_assignment" "test" { + scope = azurerm_storage_account.test.id + role_definition_name = "Storage Account Key Operator Service Role" + principal_id = data.azuread_service_principal.test.id +} + +resource "azurerm_key_vault_managed_storage_account" "test" { + name = "acctestKVstorage" + key_vault_id = azurerm_key_vault.test.id + storage_account_id = azurerm_storage_account.test.id + storage_account_key = "key1" + regenerate_key_automatically = %t + regeneration_period = "%s" + + tags = { + "hello" = "world" + } + + depends_on = [azurerm_role_assignment.test] +} +`, r.template(data), autoGenerateKey, regenPeriod) +} + +func (r KeyVaultManagedStorageAccountResource) softDeleteRecovery(data acceptance.TestData, purge bool, name string) string { + return fmt.Sprintf(` +provider "azurerm" { + features { + key_vault { + purge_soft_delete_on_destroy = "%t" + recover_soft_deleted_key_vaults = true + } + } +} + +%s + +resource "azurerm_key_vault_managed_storage_account" "test" { + name = "acctestKVstorage%s" + key_vault_id = azurerm_key_vault.test.id + storage_account_id = azurerm_storage_account.test.id + storage_account_key = "key1" + regenerate_key_automatically = false + regeneration_period = "P1D" +} +`, purge, r.template(data), name) +} + +func (KeyVaultManagedStorageAccountResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctest-kv-RG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "unlikely23exst2acct%s" + resource_group_name = azurerm_resource_group.test.name + + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_key_vault" "test" { + name = "acctestkv-%s" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" + + access_policy { + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + + secret_permissions = [ + "Get", + "Delete" + ] + + storage_permissions = [ + "Get", + "List", + "Set", + "SetSAS", + "Update", + "RegenerateKey" + ] + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomString) +} + +func (KeyVaultManagedStorageAccountResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + dataPlaneClient := client.KeyVault.ManagementClient + keyVaultsClient := client.KeyVault + + id, err := parse.ParseOptionallyVersionedNestedItemID(state.ID) + if err != nil { + return nil, err + } + + keyVaultIdRaw, err := keyVaultsClient.KeyVaultIDFromBaseUrl(ctx, client.Resource, id.KeyVaultBaseUrl) + if err != nil || keyVaultIdRaw == nil { + return nil, fmt.Errorf("retrieving the Resource ID the Key Vault at URL %q: %s", id.KeyVaultBaseUrl, err) + } + keyVaultId, err := parse.VaultID(*keyVaultIdRaw) + if err != nil { + return nil, err + } + + ok, err := keyVaultsClient.Exists(ctx, *keyVaultId) + if err != nil || !ok { + return nil, fmt.Errorf("checking if key vault %q for Managed Storage Account %q in Vault at url %q exists: %v", *keyVaultId, id.Name, id.KeyVaultBaseUrl, err) + } + + resp, err := dataPlaneClient.GetStorageAccount(ctx, id.KeyVaultBaseUrl, id.Name) + if err != nil { + return nil, fmt.Errorf("retrieving Key Vault Managed Storage Account %q: %+v", state.ID, err) + } + + return utils.Bool(resp.ID != nil), nil +} diff --git a/internal/services/keyvault/parse/sas_definition.go b/internal/services/keyvault/parse/sas_definition.go new file mode 100644 index 000000000000..f3b2f68f3d96 --- /dev/null +++ b/internal/services/keyvault/parse/sas_definition.go @@ -0,0 +1,44 @@ +package parse + +import ( + "fmt" + "net/url" + "strings" +) + +type SasDefinitionId struct { + KeyVaultBaseUrl string + StorageAccountName string + Name string +} + +func SasDefinitionID(id string) (*SasDefinitionId, error) { + // example: https://example-keyvault.vault.azure.net/storage/exampleStorageAcc01/sas/exampleSasDefinition01 + idURL, err := url.ParseRequestURI(id) + if err != nil { + return nil, fmt.Errorf("Cannot parse Azure KeyVault Managed Storage Sas Definition Id: %s", err) + } + + path := idURL.Path + + path = strings.TrimPrefix(path, "/") + path = strings.TrimSuffix(path, "/") + + components := strings.Split(path, "/") + + if len(components) != 4 { + return nil, fmt.Errorf("Key Vault Managed Storage Sas Definition ID should have 4 segments, got %d: '%s'", len(components), path) + } + + if components[0] != "storage" || components[2] != "sas" { + return nil, fmt.Errorf("Key Vault Managed Storage Sas Definition ID path must begin with %q", "/storage/exampleStorageAcc01/sas") + } + + sasDefinitionId := SasDefinitionId{ + KeyVaultBaseUrl: fmt.Sprintf("%s://%s/", idURL.Scheme, idURL.Host), + StorageAccountName: components[1], + Name: components[3], + } + + return &sasDefinitionId, nil +} diff --git a/internal/services/keyvault/registration.go b/internal/services/keyvault/registration.go index f4a9f62ee6ea..987c369478ab 100644 --- a/internal/services/keyvault/registration.go +++ b/internal/services/keyvault/registration.go @@ -36,12 +36,14 @@ func (r Registration) SupportedDataSources() map[string]*pluginsdk.Resource { // SupportedResources returns the supported Resources supported by this Service func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { return map[string]*pluginsdk.Resource{ - "azurerm_key_vault_access_policy": resourceKeyVaultAccessPolicy(), - "azurerm_key_vault_certificate": resourceKeyVaultCertificate(), - "azurerm_key_vault_certificate_issuer": resourceKeyVaultCertificateIssuer(), - "azurerm_key_vault_key": resourceKeyVaultKey(), - "azurerm_key_vault_managed_hardware_security_module": resourceKeyVaultManagedHardwareSecurityModule(), - "azurerm_key_vault_secret": resourceKeyVaultSecret(), - "azurerm_key_vault": resourceKeyVault(), + "azurerm_key_vault_access_policy": resourceKeyVaultAccessPolicy(), + "azurerm_key_vault_certificate": resourceKeyVaultCertificate(), + "azurerm_key_vault_certificate_issuer": resourceKeyVaultCertificateIssuer(), + "azurerm_key_vault_key": resourceKeyVaultKey(), + "azurerm_key_vault_managed_hardware_security_module": resourceKeyVaultManagedHardwareSecurityModule(), + "azurerm_key_vault_secret": resourceKeyVaultSecret(), + "azurerm_key_vault": resourceKeyVault(), + "azurerm_key_vault_managed_storage_account": resourceKeyVaultManagedStorageAccount(), + "azurerm_key_vault_managed_storage_account_sas_token_definition": resourceKeyVaultManagedStorageAccountSasTokenDefinition(), } } diff --git a/website/docs/r/key_vault_managed_storage_account.html.markdown b/website/docs/r/key_vault_managed_storage_account.html.markdown new file mode 100644 index 000000000000..c3d4687b25c4 --- /dev/null +++ b/website/docs/r/key_vault_managed_storage_account.html.markdown @@ -0,0 +1,239 @@ +--- +subcategory: "Key Vault" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_key_vault_managed_storage_account" +description: |- + Manages a Key Vault Managed Storage Account. +--- + +# azurerm_key_vault_managed_storage_account + +Manages a Key Vault Managed Storage Account. + +## Example Usage + +```hcl +data "azurerm_client_config" "example" {} + +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_storage_account" "example" { + name = "storageaccountname" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +data "azurerm_storage_account_sas" "example" { + connection_string = azurerm_storage_account.example.primary_connection_string + https_only = true + + resource_types { + service = true + container = false + object = false + } + + services { + blob = true + queue = false + table = false + file = false + } + + start = "2021-04-30T00:00:00Z" + expiry = "2023-04-30T00:00:00Z" + + permissions { + read = true + write = true + delete = false + list = false + add = true + create = true + update = false + process = false + } +} + +resource "azurerm_key_vault" "example" { + name = "" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" + + access_policy { + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + + secret_permissions = [ + "Get", + "Delete" + ] + + storage_permissions = [ + "Get", + "List", + "Set", + "SetSAS", + "GetSAS", + "DeleteSAS", + "Update", + "RegenerateKey" + ] + } +} + +resource "azurerm_key_vault_managed_storage_account" "example" { + name = "examplemanagedstorage" + key_vault_id = azurerm_key_vault.example.id + storage_account_id = azurerm_storage_account.example.id + storage_account_key = "key1" + regenerate_key_automatically = false +} +``` + +## Example Usage (automatically regenerate Storage Account access key) + +```hcl +data "azurerm_client_config" "example" {} + +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_storage_account" "example" { + name = "storageaccountname" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +data "azurerm_storage_account_sas" "example" { + connection_string = azurerm_storage_account.example.primary_connection_string + https_only = true + + resource_types { + service = true + container = false + object = false + } + + services { + blob = true + queue = false + table = false + file = false + } + + start = "2021-04-30T00:00:00Z" + expiry = "2023-04-30T00:00:00Z" + + permissions { + read = true + write = true + delete = false + list = false + add = true + create = true + update = false + process = false + } +} + +resource "azurerm_key_vault" "example" { + name = "" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" + + access_policy { + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + + secret_permissions = [ + "Get", + "Delete" + ] + + storage_permissions = [ + "Get", + "List", + "Set", + "SetSAS", + "GetSAS", + "DeleteSAS", + "Update", + "RegenerateKey" + ] + } +} + +resource "azurerm_role_assignment" "example" { + scope = azurerm_storage_account.example.id + role_definition_name = "Storage Account Key Operator Service Role" + principal_id = "727055f9-0386-4ccb-bcf1-9237237ee102" +} + +resource "azurerm_key_vault_managed_storage_account" "example" { + name = "examplemanagedstorage" + key_vault_id = azurerm_key_vault.example.id + storage_account_id = azurerm_storage_account.example.id + storage_account_key = "key1" + regenerate_key_automatically = true + regeneration_period = "P1D" +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `name` - (Required) The name which should be used for this Key Vault Managed Storage Account. Changing this forces a new Key Vault Managed Storage Account to be created. + +* `key_vault_id` - (Required) The ID of the Key Vault where the Managed Storage Account should be created. Changing this forces a new resource to be created. + +* `storage_account_id` - (Required) The ID of the Storage Account. + +* `storage_account_key` - (Required) Which Storage Account access key that is managed by Key Vault. Possible values are `key1` and `key2`. + +--- + +* `regenerate_key_automatically` - (Optional) Should Storage Account access key be regenerated periodically? + +~> **NOTE:** Azure Key Vault application needs to have access to Storage Account for auto regeneration to work. Example can be found above. + +* `regeneration_period` - (Optional) How often Storage Account access key should be regenerated. Value needs to be in [ISO 8601 duration format](https://en.wikipedia.org/wiki/ISO_8601#Durations). + +* `tags` - (Optional) A mapping of tags which should be assigned to the Key Vault Managed Storage Account. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Key Vault Managed Storage Account. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Key Vault Managed Storage Account. +* `read` - (Defaults to 5 minutes) Used when retrieving the Key Vault Managed Storage Account. +* `update` - (Defaults to 30 minutes) Used when updating the Key Vault Managed Storage Account. +* `delete` - (Defaults to 30 minutes) Used when deleting the Key Vault Managed Storage Account. + +## Import + +Key Vault Managed Storage Accounts can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_key_vault_managed_storage_account.example https://example-keyvault.vault.azure.net/storage/exampleStorageAcc01 +``` diff --git a/website/docs/r/key_vault_managed_storage_account_sas_token_definition.html.markdown b/website/docs/r/key_vault_managed_storage_account_sas_token_definition.html.markdown new file mode 100644 index 000000000000..e4fd11b43f2d --- /dev/null +++ b/website/docs/r/key_vault_managed_storage_account_sas_token_definition.html.markdown @@ -0,0 +1,151 @@ +--- +subcategory: "Key Vault" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_key_vault_managed_storage_account_sas_token_definition" +description: |- + Manages a Key Vault Managed Storage Account SAS Definition. +--- + +# azurerm_key_vault_managed_storage_account_sas_token_definition + +Manages a Key Vault Managed Storage Account SAS Definition. + +## Example Usage + +```hcl +data "azurerm_client_config" "example" {} + +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_storage_account" "example" { + name = "storageaccountname" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +data "azurerm_storage_account_sas" "example" { + connection_string = azurerm_storage_account.example.primary_connection_string + https_only = true + + resource_types { + service = true + container = false + object = false + } + + services { + blob = true + queue = false + table = false + file = false + } + + start = "2021-04-30T00:00:00Z" + expiry = "2023-04-30T00:00:00Z" + + permissions { + read = true + write = true + delete = false + list = false + add = true + create = true + update = false + process = false + } +} + +resource "azurerm_key_vault" "example" { + name = "" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + tenant_id = data.azurerm_client_config.current.tenant_id + sku_name = "standard" + + access_policy { + tenant_id = data.azurerm_client_config.current.tenant_id + object_id = data.azurerm_client_config.current.object_id + + secret_permissions = [ + "Get", + "Delete" + ] + + storage_permissions = [ + "Get", + "List", + "Set", + "SetSAS", + "GetSAS", + "DeleteSAS", + "Update", + "RegenerateKey" + ] + } +} + +resource "azurerm_key_vault_managed_storage_account" "test" { + name = "examplemanagedstorage" + key_vault_id = azurerm_key_vault.example.id + storage_account_id = azurerm_storage_account.example.id + storage_account_key = "key1" + regenerate_key_automatically = false + regeneration_period = "P1D" +} + +resource "azurerm_key_vault_managed_storage_account_sas_token_definition" "example" { + name = "examplesasdefinition" + validity_period = "P1D" + managed_storage_account_id = azurerm_key_vault_managed_storage_account.example.id + sas_template_uri = data.azurerm_storage_account_sas.example.sas + sas_type = "account" +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `name` - (Required) The name which should be used for this SAS Definition. + +* `managed_storage_account_id` - (Required) The ID of the Managed Storage Account. + +* `sas_template_uri` - (Required) The SAS definition token template signed with an arbitrary key. Tokens created according to the SAS definition will have the same properties as the template, but regenerated with a new validaty period. + +* `sas_type` - (Required) The type of SAS token the SAS definition will create. Possible values are `account` and `service`. + +* `validity_period` - (Required) Validity period of SAS token. Value needs to be in [ISO 8601 duration format](https://en.wikipedia.org/wiki/ISO_8601#Durations). + +--- + +* `tags` - (Optional) A mapping of tags which should be assigned to the SAS Definition. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Managed Storage Account SAS Definition. + +* `secret_id` - The ID of the Secret that is created by Managed Storage Account SAS Definition. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Key Vault. +* `read` - (Defaults to 5 minutes) Used when retrieving the Key Vault. +* `update` - (Defaults to 30 minutes) Used when updating the Key Vault. +* `delete` - (Defaults to 30 minutes) Used when deleting the Key Vault. + +## Import + +Key Vaults can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_key_vault_managed_storage_account_sas_token_definition.example https://example-keyvault.vault.azure.net/storage/exampleStorageAcc01/sas/exampleSasDefinition01 +```