diff --git a/internal/services/dataprotection/data_protection_backup_instance_kubernetes_cluster_resource.go b/internal/services/dataprotection/data_protection_backup_instance_kubernetes_cluster_resource.go new file mode 100644 index 000000000000..41d7ffa9576e --- /dev/null +++ b/internal/services/dataprotection/data_protection_backup_instance_kubernetes_cluster_resource.go @@ -0,0 +1,354 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataprotection + +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/commonids" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2023-05-01/backupinstances" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2023-05-01/backuppolicies" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + resourceParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/resource/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" +) + +type BackupInstanceKubernatesClusterModel struct { + Name string `tfschema:"name"` + Location string `tfschema:"location"` + VaultId string `tfschema:"vault_id"` + BackupPolicyId string `tfschema:"backup_policy_id"` + KubernetesClusterId string `tfschema:"kubernetes_cluster_id"` + SnapshotResourceGroupName string `tfschema:"snapshot_resource_group_name"` + BackupDatasourceParameters []BackupDatasourceParameters `tfschema:"backup_datasource_parameters"` +} + +type BackupDatasourceParameters struct { + IncludedNamespaces []string `tfschema:"included_namespaces"` + IncludedResourceTypes []string `tfschema:"included_resource_types"` + ExcludedNamespaces []string `tfschema:"excluded_namespaces"` + ExcludedResourceTypes []string `tfschema:"excluded_resource_types"` + LabelSelectors []string `tfschema:"label_selectors"` + VolumeSnapshotEnabled bool `tfschema:"volume_snapshot_enabled"` + ClusterScopeResourceEnabled bool `tfschema:"cluster_scoped_resources_enabled"` +} + +type DataProtectionBackupInstanceKubernatesClusterResource struct{} + +var _ sdk.Resource = DataProtectionBackupInstanceKubernatesClusterResource{} + +func (r DataProtectionBackupInstanceKubernatesClusterResource) ResourceType() string { + return "azurerm_data_protection_backup_instance_kubernetes_cluster" +} + +func (r DataProtectionBackupInstanceKubernatesClusterResource) ModelObject() interface{} { + return &BackupInstanceKubernatesClusterModel{} +} + +func (r DataProtectionBackupInstanceKubernatesClusterResource) IDValidationFunc() pluginsdk.SchemaValidateFunc { + return backupinstances.ValidateBackupInstanceID +} + +func (r DataProtectionBackupInstanceKubernatesClusterResource) Arguments() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + }, + + "location": commonschema.Location(), + + "vault_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: backupinstances.ValidateBackupVaultID, + }, + + "backup_policy_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: backuppolicies.ValidateBackupPolicyID, + }, + + "kubernetes_cluster_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: commonids.ValidateKubernetesClusterID, + }, + + "snapshot_resource_group_name": commonschema.ResourceGroupName(), + + "backup_datasource_parameters": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "excluded_namespaces": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "excluded_resource_types": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "cluster_scoped_resources_enabled": { + Type: pluginsdk.TypeBool, + ForceNew: true, + Optional: true, + Default: false, + }, + "included_namespaces": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "included_resource_types": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "label_selectors": { + Type: pluginsdk.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "volume_snapshot_enabled": { + Type: pluginsdk.TypeBool, + ForceNew: true, + Optional: true, + Default: false, + }, + }, + }, + }, + } +} + +func (r DataProtectionBackupInstanceKubernatesClusterResource) Attributes() map[string]*pluginsdk.Schema { + return map[string]*pluginsdk.Schema{} +} + +func (r DataProtectionBackupInstanceKubernatesClusterResource) Create() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + var model BackupInstanceKubernatesClusterModel + if err := metadata.Decode(&model); err != nil { + return fmt.Errorf("decoding: %+v", err) + } + + client := metadata.Client.DataProtection.BackupInstanceClient + + vaultId, err := backupinstances.ParseBackupVaultID(model.VaultId) + if err != nil { + return err + } + + id := backupinstances.NewBackupInstanceID(vaultId.SubscriptionId, vaultId.ResourceGroupName, vaultId.BackupVaultName, model.Name) + existing, err := client.Get(ctx, id) + if err != nil { + if !response.WasNotFound(existing.HttpResponse) { + return fmt.Errorf("checking for existing %s: %+v", id, err) + } + } + + if !response.WasNotFound(existing.HttpResponse) { + return metadata.ResourceRequiresImport(r.ResourceType(), id) + } + + policyId, err := backuppolicies.ParseBackupPolicyID(model.BackupPolicyId) + if err != nil { + return err + } + + aksId, err := commonids.ParseKubernetesClusterID(model.KubernetesClusterId) + if err != nil { + return err + } + + snapshotResourceGroupId := resourceParse.NewResourceGroupID(metadata.Client.Account.SubscriptionId, model.SnapshotResourceGroupName) + parameters := backupinstances.BackupInstanceResource{ + Properties: &backupinstances.BackupInstance{ + DataSourceInfo: backupinstances.Datasource{ + DatasourceType: pointer.To("Microsoft.ContainerService/managedClusters"), + ObjectType: pointer.To("Datasource"), + ResourceID: aksId.ID(), + ResourceLocation: pointer.To(location.Normalize(model.Location)), + ResourceName: pointer.To(aksId.ManagedClusterName), + ResourceType: pointer.To("Microsoft.ContainerService/managedClusters"), + ResourceUri: pointer.To(aksId.ID()), + }, + FriendlyName: pointer.To(id.BackupInstanceName), + ObjectType: "BackupInstance", + PolicyInfo: backupinstances.PolicyInfo{ + PolicyId: policyId.ID(), + PolicyParameters: &backupinstances.PolicyParameters{ + DataStoreParametersList: &[]backupinstances.DataStoreParameters{ + backupinstances.AzureOperationalStoreParameters{ + ResourceGroupId: pointer.To(snapshotResourceGroupId.ID()), + DataStoreType: backupinstances.DataStoreTypesOperationalStore, + }, + }, + BackupDatasourceParametersList: expandBackupDatasourceParameters(model.BackupDatasourceParameters), + }, + }, + }, + } + + if err := client.CreateOrUpdateThenPoll(ctx, id, parameters); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + metadata.SetID(id) + return nil + }, + } +} + +func (r DataProtectionBackupInstanceKubernatesClusterResource) Read() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 5 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.DataProtection.BackupInstanceClient + + id, err := backupinstances.ParseBackupInstanceID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return metadata.MarkAsGone(*id) + } + + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + vaultId := backupinstances.NewBackupVaultID(id.SubscriptionId, id.ResourceGroupName, id.BackupVaultName) + + state := BackupInstanceKubernatesClusterModel{ + Name: id.BackupInstanceName, + VaultId: vaultId.ID(), + } + + if model := resp.Model; model != nil { + if properties := model.Properties; properties != nil { + state.Location = location.NormalizeNilable(properties.DataSourceInfo.ResourceLocation) + state.BackupPolicyId = properties.PolicyInfo.PolicyId + state.KubernetesClusterId = properties.DataSourceInfo.ResourceID + + if policyParameters := properties.PolicyInfo.PolicyParameters; policyParameters != nil { + if dataStorePara := policyParameters.DataStoreParametersList; dataStorePara != nil { + if dsp := pointer.From(dataStorePara); len(dsp) > 0 { + if parameter, ok := dsp[0].(backupinstances.AzureOperationalStoreParameters); ok && parameter.ResourceGroupId != nil { + resourceGroupId, err := resourceParse.ResourceGroupID(*parameter.ResourceGroupId) + if err != nil { + return err + } + state.SnapshotResourceGroupName = resourceGroupId.ResourceGroup + } + } + } + if backupDsp := policyParameters.BackupDatasourceParametersList; backupDsp != nil { + if v := flattenBackupDatasourceParameters(*backupDsp); v != nil { + state.BackupDatasourceParameters = pointer.From(v) + } + } + } + } + } + + return metadata.Encode(&state) + }, + } +} + +func (r DataProtectionBackupInstanceKubernatesClusterResource) Delete() sdk.ResourceFunc { + return sdk.ResourceFunc{ + Timeout: 30 * time.Minute, + Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error { + client := metadata.Client.DataProtection.BackupInstanceClient + + id, err := backupinstances.ParseBackupInstanceID(metadata.ResourceData.Id()) + if err != nil { + return err + } + + err = client.DeleteThenPoll(ctx, *id) + if err != nil { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + + return nil + }, + } +} + +func expandBackupDatasourceParameters(input []BackupDatasourceParameters) *[]backupinstances.BackupDatasourceParameters { + if len(input) == 0 { + return nil + } + results := make([]backupinstances.BackupDatasourceParameters, 0) + results = append(results, backupinstances.KubernetesClusterBackupDatasourceParameters{ + ExcludedNamespaces: pointer.To(input[0].ExcludedNamespaces), + ExcludedResourceTypes: pointer.To(input[0].ExcludedResourceTypes), + IncludeClusterScopeResources: input[0].ClusterScopeResourceEnabled, + IncludedNamespaces: pointer.To(input[0].IncludedNamespaces), + IncludedResourceTypes: pointer.To(input[0].IncludedResourceTypes), + LabelSelectors: pointer.To(input[0].LabelSelectors), + SnapshotVolumes: input[0].VolumeSnapshotEnabled, + }) + return &results +} + +func flattenBackupDatasourceParameters(input []backupinstances.BackupDatasourceParameters) *[]BackupDatasourceParameters { + results := make([]BackupDatasourceParameters, 0) + if len(input) == 0 { + return &results + } + + if item, ok := input[0].(backupinstances.KubernetesClusterBackupDatasourceParameters); ok { + results = append(results, BackupDatasourceParameters{ + ExcludedNamespaces: pointer.From(item.ExcludedNamespaces), + ExcludedResourceTypes: pointer.From(item.ExcludedResourceTypes), + ClusterScopeResourceEnabled: item.IncludeClusterScopeResources, + IncludedNamespaces: pointer.From(item.IncludedNamespaces), + IncludedResourceTypes: pointer.From(item.IncludedResourceTypes), + LabelSelectors: pointer.From(item.LabelSelectors), + VolumeSnapshotEnabled: item.SnapshotVolumes, + }) + } + return &results +} diff --git a/internal/services/dataprotection/data_protection_backup_instance_kubernetes_cluster_resource_test.go b/internal/services/dataprotection/data_protection_backup_instance_kubernetes_cluster_resource_test.go new file mode 100644 index 000000000000..366c509e4025 --- /dev/null +++ b/internal/services/dataprotection/data_protection_backup_instance_kubernetes_cluster_resource_test.go @@ -0,0 +1,282 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package dataprotection_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-sdk/resource-manager/dataprotection/2023-05-01/backupinstances" + "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/tf/pluginsdk" +) + +type DataProtectionBackupInstanceKubernatesClusterTestResource struct{} + +func TestAccDataProtectionBackupInstanceKubernatesCluster_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_protection_backup_instance_kubernetes_cluster", "test") + r := DataProtectionBackupInstanceKubernatesClusterTestResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccDataProtectionBackupInstanceKubernatesCluster_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_protection_backup_instance_kubernetes_cluster", "test") + r := DataProtectionBackupInstanceKubernatesClusterTestResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccDataProtectionBackupInstanceKubernatesCluster_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_data_protection_backup_instance_kubernetes_cluster", "test") + r := DataProtectionBackupInstanceKubernatesClusterTestResource{} + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r DataProtectionBackupInstanceKubernatesClusterTestResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := backupinstances.ParseBackupInstanceID(state.ID) + if err != nil { + return nil, err + } + resp, err := client.DataProtection.BackupInstanceClient.Get(ctx, *id) + if err != nil { + if response.WasNotFound(resp.HttpResponse) { + return pointer.To(false), nil + } + return nil, fmt.Errorf("retrieving %s: %+v", *id, err) + } + return pointer.To(resp.Model != nil), nil +} + +func (r DataProtectionBackupInstanceKubernatesClusterTestResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctest-dp-%[1]d" + location = "%[2]s" +} + +resource "azurerm_resource_group" "snap" { + name = "acctest-dp-snap-%[1]d" + location = "%[2]s" +} + +resource "azurerm_data_protection_backup_vault" "test" { + name = "acctest-dbv-%[1]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + datastore_type = "VaultStore" + redundancy = "LocallyRedundant" + soft_delete = "Off" + identity { + type = "SystemAssigned" + } +} + +resource "azurerm_kubernetes_cluster" "test" { + name = "acctestaks%[1]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + dns_prefix = "acctestaks%[1]d" + + default_node_pool { + name = "default" + node_count = 1 + vm_size = "Standard_DS2_v2" + enable_host_encryption = true + } + + identity { + type = "SystemAssigned" + } +} + +resource "azurerm_kubernetes_cluster_trusted_access_role_binding" "test_aks_cluster_trusted_access" { + kubernetes_cluster_id = azurerm_kubernetes_cluster.test.id + name = "mayankta" + roles = ["Microsoft.DataProtection/backupVaults/backup-operator"] + source_resource_id = azurerm_data_protection_backup_vault.test.id +} + +resource "azurerm_storage_account" "test" { + name = "acctest%[3]s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_storage_container" "test" { + name = "testaccsc%[3]s" + storage_account_name = azurerm_storage_account.test.name + container_access_type = "private" +} + +resource "azurerm_kubernetes_cluster_extension" "test" { + name = "acctest-kce-%[1]d" + cluster_id = azurerm_kubernetes_cluster.test.id + extension_type = "Microsoft.DataProtection.Kubernetes" + release_train = "stable" + release_namespace = "dataprotection-microsoft" + configuration_settings = { + "configuration.backupStorageLocation.bucket" = azurerm_storage_container.test.name + "configuration.backupStorageLocation.config.resourceGroup" = azurerm_resource_group.test.name + "configuration.backupStorageLocation.config.storageAccount" = azurerm_storage_account.test.name + "configuration.backupStorageLocation.config.subscriptionId" = data.azurerm_client_config.current.subscription_id + "credentials.tenantId" = data.azurerm_client_config.current.tenant_id + } +} + +resource "azurerm_role_assignment" "test_extension_and_storage_account_permission" { + scope = azurerm_storage_account.test.id + role_definition_name = "Storage Account Contributor" + principal_id = azurerm_kubernetes_cluster_extension.test.aks_assigned_identity[0].principal_id +} + +resource "azurerm_role_assignment" "test_vault_msi_read_on_cluster" { + scope = azurerm_kubernetes_cluster.test.id + role_definition_name = "Reader" + principal_id = azurerm_data_protection_backup_vault.test.identity[0].principal_id +} + +resource "azurerm_role_assignment" "test_vault_msi_read_on_snap_rg" { + scope = azurerm_resource_group.snap.id + role_definition_name = "Reader" + principal_id = azurerm_data_protection_backup_vault.test.identity[0].principal_id +} + +resource "azurerm_role_assignment" "test_cluster_msi_contributor_on_snap_rg" { + scope = azurerm_resource_group.snap.id + role_definition_name = "Contributor" + principal_id = azurerm_kubernetes_cluster.test.identity[0].principal_id +} + +resource "azurerm_data_protection_backup_policy_kubernetes_cluster" "test" { + name = "acctest-paks-%[1]d" + resource_group_name = azurerm_resource_group.test.name + vault_name = azurerm_data_protection_backup_vault.test.name + + backup_repeating_time_intervals = ["R/2021-05-23T02:30:00+00:00/P1W"] + + retention_rule { + name = "Daily" + priority = 25 + + life_cycle { + duration = "P84D" + data_store_type = "OperationalStore" + } + + criteria { + days_of_week = ["Thursday"] + months_of_year = ["November"] + weeks_of_month = ["First"] + scheduled_backup_times = ["2021-05-23T02:30:00Z"] + } + } + + default_retention_rule { + life_cycle { + duration = "P14D" + data_store_type = "OperationalStore" + } + } +} + +`, data.RandomInteger, data.Locations.Primary, data.RandomString) +} + +func (r DataProtectionBackupInstanceKubernatesClusterTestResource) requiresImport(data acceptance.TestData) string { + config := r.basic(data) + return fmt.Sprintf(` +%s + +resource "azurerm_data_protection_backup_instance_kubernetes_cluster" "import" { + name = azurerm_data_protection_backup_instance_kubernetes_cluster.test.name + location = azurerm_data_protection_backup_instance_kubernetes_cluster.test.location + vault_id = azurerm_data_protection_backup_instance_kubernetes_cluster.test.vault_id + backup_policy_id = azurerm_data_protection_backup_instance_kubernetes_cluster.test.backup_policy_id + kubernetes_cluster_id = azurerm_data_protection_backup_instance_kubernetes_cluster.test.kubernetes_cluster_id + snapshot_resource_group_name = azurerm_data_protection_backup_instance_kubernetes_cluster.test.snapshot_resource_group_name +} +`, config) +} + +func (r DataProtectionBackupInstanceKubernatesClusterTestResource) basic(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_data_protection_backup_instance_kubernetes_cluster" "test" { + name = "acctest-iaks-%[2]d" + location = azurerm_resource_group.test.location + vault_id = azurerm_data_protection_backup_vault.test.id + backup_policy_id = azurerm_data_protection_backup_policy_kubernetes_cluster.test.id + kubernetes_cluster_id = azurerm_kubernetes_cluster.test.id + snapshot_resource_group_name = azurerm_resource_group.snap.name +} +`, template, data.RandomInteger) +} + +func (r DataProtectionBackupInstanceKubernatesClusterTestResource) complete(data acceptance.TestData) string { + template := r.template(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_data_protection_backup_instance_kubernetes_cluster" "test" { + name = "acctest-iaks-%[2]d" + location = azurerm_resource_group.test.location + vault_id = azurerm_data_protection_backup_vault.test.id + backup_policy_id = azurerm_data_protection_backup_policy_kubernetes_cluster.test.id + kubernetes_cluster_id = azurerm_kubernetes_cluster.test.id + snapshot_resource_group_name = azurerm_resource_group.snap.name + + backup_datasource_parameters { + excluded_namespaces = ["test-excluded-namespaces"] + excluded_resource_types = ["exvolumesnapshotcontents.snapshot.storage.k8s.io"] + cluster_scoped_resources_enabled = true + included_namespaces = ["test-included-namespaces"] + included_resource_types = ["involumesnapshotcontents.snapshot.storage.k8s.io"] + label_selectors = ["kubernetes.io/metadata.name:test"] + volume_snapshot_enabled = false + } + + depends_on = [ + azurerm_role_assignment.test_extension_and_storage_account_permission, + ] +} +`, template, data.RandomInteger) +} diff --git a/internal/services/dataprotection/registration.go b/internal/services/dataprotection/registration.go index a6ab55f74a20..2cf315a2c4c2 100644 --- a/internal/services/dataprotection/registration.go +++ b/internal/services/dataprotection/registration.go @@ -59,5 +59,6 @@ func (r Registration) DataSources() []sdk.DataSource { func (r Registration) Resources() []sdk.Resource { return []sdk.Resource{ DataProtectionBackupPolicyKubernatesClusterResource{}, + DataProtectionBackupInstanceKubernatesClusterResource{}, } } diff --git a/website/docs/r/data_protection_backup_instance_kubernetes_cluster.html.markdown b/website/docs/r/data_protection_backup_instance_kubernetes_cluster.html.markdown new file mode 100644 index 000000000000..96c4a687a0fd --- /dev/null +++ b/website/docs/r/data_protection_backup_instance_kubernetes_cluster.html.markdown @@ -0,0 +1,231 @@ +--- +subcategory: "DataProtection" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_data_protection_backup_instance_kubernetes_cluster" +description: |- + Manages a Backup Instance to back up a Kubernetes Cluster. +--- + +# azurerm_data_protection_backup_instance_kubernetes_cluster + +Manages a Backup Instance to back up a Kubernetes Cluster. + +## Example Usage + +```hcl +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "example" { + name = "example" + location = "West Europe" +} + +resource "azurerm_resource_group" "snap" { + name = "example-snap" + location = "West Europe" +} + +resource "azurerm_data_protection_backup_vault" "example" { + name = "example" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + datastore_type = "VaultStore" + redundancy = "LocallyRedundant" + + identity { + type = "SystemAssigned" + } +} + +resource "azurerm_kubernetes_cluster" "example" { + name = "example" + location = azurerm_resource_group.example.location + resource_group_name = azurerm_resource_group.example.name + dns_prefix = "dns" + + default_node_pool { + name = "default" + node_count = 1 + vm_size = "Standard_DS2_v2" + enable_host_encryption = true + } + + identity { + type = "SystemAssigned" + } +} + +resource "azurerm_kubernetes_cluster_trusted_access_role_binding" "aks_cluster_trusted_access" { + kubernetes_cluster_id = azurerm_kubernetes_cluster.example.id + name = "example" + roles = ["Microsoft.DataProtection/backupVaults/backup-operator"] + source_resource_id = azurerm_data_protection_backup_vault.example.id +} + +resource "azurerm_storage_account" "example" { + name = "example" + resource_group_name = azurerm_resource_group.example.name + location = azurerm_resource_group.example.location + account_tier = "Standard" + account_replication_type = "LRS" +} + +resource "azurerm_storage_container" "example" { + name = "example" + storage_account_name = azurerm_storage_account.example.name + container_access_type = "private" +} + +resource "azurerm_kubernetes_cluster_extension" "example" { + name = "example" + cluster_id = azurerm_kubernetes_cluster.example.id + extension_type = "Microsoft.DataProtection.Kubernetes" + release_train = "stable" + release_namespace = "dataprotection-microsoft" + configuration_settings = { + "configuration.backupStorageLocation.bucket" = azurerm_storage_container.example.name + "configuration.backupStorageLocation.config.resourceGroup" = azurerm_resource_group.example.name + "configuration.backupStorageLocation.config.storageAccount" = azurerm_storage_account.example.name + "configuration.backupStorageLocation.config.subscriptionId" = data.azurerm_client_config.current.subscription_id + "credentials.tenantId" = data.azurerm_client_config.current.tenant_id + } +} + +resource "azurerm_role_assignment" "extension_and_storage_account_permission" { + scope = azurerm_storage_account.example.id + role_definition_name = "Storage Account Contributor" + principal_id = azurerm_kubernetes_cluster_extension.example.aks_assigned_identity[0].principal_id +} + +resource "azurerm_role_assignment" "vault_msi_read_on_cluster" { + scope = azurerm_kubernetes_cluster.example.id + role_definition_name = "Reader" + principal_id = azurerm_data_protection_backup_vault.example.identity[0].principal_id +} + +resource "azurerm_role_assignment" "vault_msi_read_on_snap_rg" { + scope = azurerm_resource_group.snap.id + role_definition_name = "Reader" + principal_id = azurerm_data_protection_backup_vault.example.identity[0].principal_id +} + +resource "azurerm_role_assignment" "cluster_msi_contributor_on_snap_rg" { + scope = azurerm_resource_group.snap.id + role_definition_name = "Contributor" + principal_id = azurerm_kubernetes_cluster.example.identity[0].principal_id +} + +resource "azurerm_data_protection_backup_policy_kubernetes_cluster" "example" { + name = "example" + resource_group_name = azurerm_resource_group.example.name + vault_name = azurerm_data_protection_backup_vault.example.name + + backup_repeating_time_intervals = ["R/2023-05-23T02:30:00+00:00/P1W"] + + retention_rule { + name = "Daily" + priority = 25 + + life_cycle { + duration = "P84D" + data_store_type = "OperationalStore" + } + + criteria { + days_of_week = ["Thursday"] + months_of_year = ["November"] + weeks_of_month = ["First"] + scheduled_backup_times = ["2023-05-23T02:30:00Z"] + } + } + + default_retention_rule { + life_cycle { + duration = "P14D" + data_store_type = "OperationalStore" + } + } +} + +resource "azurerm_data_protection_backup_instance_kubernetes_cluster" "example" { + name = "example" + location = azurerm_resource_group.example.location + vault_id = azurerm_data_protection_backup_vault.example.id + kubernetes_cluster_id = azurerm_kubernetes_cluster.example.id + snapshot_resource_group_name = azurerm_resource_group.snap.name + backup_policy_id = azurerm_data_protection_backup_policy_kubernetes_cluster.example.id + + backup_datasource_parameters { + excluded_namespaces = ["test-excluded-namespaces"] + excluded_resource_types = ["exvolumesnapshotcontents.snapshot.storage.k8s.io"] + cluster_scoped_resources_enabled = true + included_namespaces = ["test-included-namespaces"] + included_resource_types = ["involumesnapshotcontents.snapshot.storage.k8s.io"] + label_selectors = ["kubernetes.io/metadata.name:test"] + volume_snapshot_enabled = true + } + + depends_on = [ + azurerm_role_assignment.extension_and_storage_account_permission, + ] +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `name` - (Required) The name which should be used for this Backup Instance Kubernetes Cluster. Changing this forces a new resource to be created. + +* `location` - (Required) The location of the Backup Instance Kubernetes Cluster. Changing this forces a new resource to be created. + +* `vault_id` - (Required) The ID of the Backup Vault within which the Backup Instance Kubernetes Cluster should exist. Changing this forces a new resource to be created. + +* `backup_policy_id` - (Required) The ID of the Backup Policy. Changing this forces a new resource to be created. + +* `kubernetes_cluster_id` - (Required) The ID of the Kubernetes Cluster. Changing this forces a new resource to be created. + +* `snapshot_resource_group_name` - (Required) The name of the Resource Group where snapshots are stored. Changing this forces a new resource to be created. + +* `backup_datasource_parameters` - (Optional) A `backup_datasource_parameters` block as defined below. + +--- + +A `backup_datasource_parameters` block supports the following: + +* `excluded_namespaces` - (Optional) Specifies the namespaces to be excluded during backup. Changing this forces a new resource to be created. + +* `excluded_resource_types` - (Optional) Specifies the resource types to be excluded during backup. Changing this forces a new resource to be created. + +* `cluster_scoped_resources_enabled` - (Optional) Whether to include cluster scope resources during backup. Default to `false`. Changing this forces a new resource to be created. + +* `included_namespaces` - (Optional) Specifies the namespaces to be included during backup. Changing this forces a new resource to be created. + +* `included_resource_types` - (Optional) Specifies the resource types to be included during backup. Changing this forces a new resource to be created. + +* `label_selectors` - (Optional) Specifies the resources with such label selectors to be included during backup. Changing this forces a new resource to be created. + +* `volume_snapshot_enabled` - (Optional) Whether to take volume snapshots during backup. Default to `false`. Changing this forces a new resource to be created. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Backup Instance Kubernetes Cluster. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/language/resources/syntax#operation-timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Backup Instance Kubernetes Cluster. +* `read` - (Defaults to 5 minutes) Used when retrieving the Backup Instance Kubernetes Cluster. +* `update` - (Defaults to 30 minutes) Used when updating the Backup Instance Kubernetes Cluster. +* `delete` - (Defaults to 30 minutes) Used when deleting the Backup Instance Kubernetes Cluster. + +## Import + +Backup Instance Kubernetes Cluster can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_data_protection_backup_instance_kubernetes_cluster.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.DataProtection/backupVaults/vault1/backupInstances/backupInstance1 +```