diff --git a/azurerm/internal/services/cosmos/cosmosdb_account_resource.go b/azurerm/internal/services/cosmos/cosmosdb_account_resource.go index bfe73870f2d5..5b684dc6f9cb 100644 --- a/azurerm/internal/services/cosmos/cosmosdb_account_resource.go +++ b/azurerm/internal/services/cosmos/cosmosdb_account_resource.go @@ -310,6 +310,40 @@ func resourceCosmosDbAccount() *schema.Resource { }, }, + "backup": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + string(documentdb.TypeContinuous), + string(documentdb.TypePeriodic), + }, false), + }, + + "interval_in_minutes": { + Type: schema.TypeInt, + Optional: true, + Default: 240, + ValidateFunc: validation.IntBetween(60, 1440), + }, + + "retention_in_hours": { + Type: schema.TypeInt, + Optional: true, + Default: 8, + ValidateFunc: validation.IntBetween(8, 720), + }, + }, + }, + }, + // computed "endpoint": { Type: schema.TypeString, @@ -487,6 +521,14 @@ func resourceCosmosDbAccountCreate(d *schema.ResourceData, meta interface{}) err } } + if v, ok := d.GetOk("backup"); ok { + policy, err := expandCosmosdbAccountBackup(v.([]interface{})) + if err != nil { + return fmt.Errorf("expanding `backup`: %+v", err) + } + account.DatabaseAccountCreateUpdateProperties.BackupPolicy = policy + } + if keyVaultKeyIDRaw, ok := d.GetOk("key_vault_key_id"); ok { keyVaultKey, err := keyVaultParse.ParseOptionallyVersionedNestedItemID(keyVaultKeyIDRaw.(string)) if err != nil { @@ -611,6 +653,14 @@ func resourceCosmosDbAccountUpdate(d *schema.ResourceData, meta interface{}) err account.DatabaseAccountCreateUpdateProperties.KeyVaultKeyURI = utils.String(keyVaultKey.ID()) } + if v, ok := d.GetOk("backup"); ok { + policy, err := expandCosmosdbAccountBackup(v.([]interface{})) + if err != nil { + return fmt.Errorf("expanding `backup`: %+v", err) + } + account.DatabaseAccountCreateUpdateProperties.BackupPolicy = policy + } + if _, err = resourceCosmosDbAccountApiUpsert(client, ctx, resourceGroup, name, account, d); err != nil { return fmt.Errorf("Error updating CosmosDB Account %q properties (Resource Group %q): %+v", name, resourceGroup, err) } @@ -740,6 +790,15 @@ func resourceCosmosDbAccountRead(d *schema.ResourceData, meta interface{}) error } d.Set("network_acl_bypass_for_azure_services", props.NetworkACLBypass == documentdb.NetworkACLBypassAzureServices) d.Set("network_acl_bypass_ids", utils.FlattenStringSlice(props.NetworkACLBypassResourceIds)) + + policy, err := flattenCosmosdbAccountBackup(props.BackupPolicy) + if err != nil { + return err + } + + if err = d.Set("backup", policy); err != nil { + return fmt.Errorf("setting `backup`: %+v", err) + } } readEndpoints := make([]string, 0) @@ -1152,3 +1211,73 @@ func resourceAzureRMCosmosDBAccountVirtualNetworkRuleHash(v interface{}) int { return schema.HashString(buf.String()) } + +func expandCosmosdbAccountBackup(input []interface{}) (documentdb.BasicBackupPolicy, error) { + if len(input) == 0 || input[0] == nil { + return nil, nil + } + attr := input[0].(map[string]interface{}) + + switch attr["type"].(string) { + case string(documentdb.TypeContinuous): + if v := attr["interval_in_minutes"].(int); v != 0 { + return nil, fmt.Errorf("`interval_in_minutes` can not be set when `type` in`backup` is `Continuous` ") + } + if v := attr["retention_in_hours"].(int); v != 0 { + return nil, fmt.Errorf("`retention_in_hours` can not be set when `type` in`backup` is `Continuous` ") + } + return documentdb.ContinuousModeBackupPolicy{ + Type: documentdb.TypeContinuous, + }, nil + + case string(documentdb.TypePeriodic): + return documentdb.PeriodicModeBackupPolicy{ + Type: documentdb.TypePeriodic, + PeriodicModeProperties: &documentdb.PeriodicModeProperties{ + BackupIntervalInMinutes: utils.Int32(int32(attr["interval_in_minutes"].(int))), + BackupRetentionIntervalInHours: utils.Int32(int32(attr["retention_in_hours"].(int))), + }, + }, nil + + default: + return nil, fmt.Errorf("unknown `type` in `backup`:%+v", attr["type"].(string)) + } +} + +func flattenCosmosdbAccountBackup(input documentdb.BasicBackupPolicy) ([]interface{}, error) { + if input == nil { + return []interface{}{}, nil + } + + switch input.(type) { + case documentdb.ContinuousModeBackupPolicy: + return []interface{}{ + map[string]interface{}{ + "type": string(documentdb.TypeContinuous), + }, + }, nil + + case documentdb.PeriodicModeBackupPolicy: + policy, ok := input.AsPeriodicModeBackupPolicy() + if !ok { + return nil, fmt.Errorf("can not transit %+v into `backup` of `type` `Periodic`", input) + } + var interval, retention int + if v := policy.PeriodicModeProperties.BackupIntervalInMinutes; v != nil { + interval = int(*v) + } + if v := policy.PeriodicModeProperties.BackupRetentionIntervalInHours; v != nil { + retention = int(*v) + } + return []interface{}{ + map[string]interface{}{ + "type": string(documentdb.TypePeriodic), + "interval_in_minutes": interval, + "retention_in_hours": retention, + }, + }, nil + + default: + return nil, fmt.Errorf("unknown `type` in `backup`: %+v", input) + } +} diff --git a/azurerm/internal/services/cosmos/cosmosdb_account_resource_test.go b/azurerm/internal/services/cosmos/cosmosdb_account_resource_test.go index 18d2565b45d1..926c36f548aa 100644 --- a/azurerm/internal/services/cosmos/cosmosdb_account_resource_test.go +++ b/azurerm/internal/services/cosmos/cosmosdb_account_resource_test.go @@ -619,6 +619,56 @@ func TestAccCosmosDBAccount_vNetFilters(t *testing.T) { }) } +func TestAccCosmosDBAccount_backup(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cosmosdb_account", "test") + r := CosmosDBAccountResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data, documentdb.GlobalDocumentDB, documentdb.Eventual), + Check: resource.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("backup.0.type").HasValue("Periodic"), + check.That(data.ResourceName).Key("backup.0.interval_in_minutes").HasValue("240"), + check.That(data.ResourceName).Key("backup.0.retention_in_hours").HasValue("8"), + ), + }, + data.ImportStep(), + { + Config: r.basicWithBackupPeriodic(data, documentdb.GlobalDocumentDB, documentdb.Eventual), + Check: resource.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basicWithBackupPeriodicUpdate(data, documentdb.GlobalDocumentDB, documentdb.Eventual), + Check: resource.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("backup.0.type").HasValue("Periodic"), + check.That(data.ResourceName).Key("backup.0.interval_in_minutes").HasValue("240"), + check.That(data.ResourceName).Key("backup.0.retention_in_hours").HasValue("8"), + ), + }, + data.ImportStep(), + }) +} + +func TestAccCosmosDBAccount_backupContinuous(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_cosmosdb_account", "test") + r := CosmosDBAccountResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basicWithBackupContinuous(data, documentdb.GlobalDocumentDB, documentdb.Eventual), + Check: resource.ComposeAggregateTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccCosmosDBAccount_networkBypass(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_cosmosdb_account", "test") r := CosmosDBAccountResource{} @@ -1660,6 +1710,110 @@ resource "azurerm_cosmosdb_account" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomString, data.RandomInteger, string(kind), string(consistency)) } +func (CosmosDBAccountResource) basicWithBackupPeriodic(data acceptance.TestData, kind documentdb.DatabaseAccountKind, consistency documentdb.DefaultConsistencyLevel) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-cosmos-%d" + location = "%s" +} + +resource "azurerm_cosmosdb_account" "test" { + name = "acctest-ca-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + offer_type = "Standard" + kind = "%s" + + consistency_policy { + consistency_level = "%s" + } + + geo_location { + location = azurerm_resource_group.test.location + failover_priority = 0 + } + + backup { + type = "Periodic" + interval_in_minutes = 120 + retention_in_hours = 10 + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, string(kind), string(consistency)) +} + +func (CosmosDBAccountResource) basicWithBackupPeriodicUpdate(data acceptance.TestData, kind documentdb.DatabaseAccountKind, consistency documentdb.DefaultConsistencyLevel) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-cosmos-%d" + location = "%s" +} + +resource "azurerm_cosmosdb_account" "test" { + name = "acctest-ca-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + offer_type = "Standard" + kind = "%s" + + consistency_policy { + consistency_level = "%s" + } + + geo_location { + location = azurerm_resource_group.test.location + failover_priority = 0 + } + + backup { + type = "Periodic" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, string(kind), string(consistency)) +} + +func (CosmosDBAccountResource) basicWithBackupContinuous(data acceptance.TestData, kind documentdb.DatabaseAccountKind, consistency documentdb.DefaultConsistencyLevel) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-cosmos-%d" + location = "%s" +} + +resource "azurerm_cosmosdb_account" "test" { + name = "acctest-ca-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + offer_type = "Standard" + kind = "%s" + + consistency_policy { + consistency_level = "%s" + } + + geo_location { + location = azurerm_resource_group.test.location + failover_priority = 0 + } + + backup { + type = "Continuous" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, string(kind), string(consistency)) +} + func (CosmosDBAccountResource) basicWithNetworkBypassTemplate(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/website/docs/r/cosmosdb_account.html.markdown b/website/docs/r/cosmosdb_account.html.markdown index 283e9a7bf583..d32b91f71d51 100644 --- a/website/docs/r/cosmosdb_account.html.markdown +++ b/website/docs/r/cosmosdb_account.html.markdown @@ -114,6 +114,8 @@ The following arguments are supported: * `network_acl_bypass_ids` - (Optional) The list of resource Ids for Network Acl Bypass for this Cosmos DB account. +* `backup` - (Optional) A `backup` block as defined below. + --- `consistency_policy` Configures the database consistency and supports the following: @@ -148,6 +150,16 @@ The following arguments are supported: * `id` - (Required) The ID of the virtual network subnet. * `ignore_missing_vnet_service_endpoint` - (Optional) If set to true, the specified subnet will be added as a virtual network rule even if its CosmosDB service endpoint is not active. Defaults to `false`. +--- + +A `backup` block supports the following: + +* `type` - (Required) The type of the `backup`. Possible values are `Continuous` and `Periodic`. Defaults to `Periodic`. + +* `interval_in_minutes` - (Optional) The interval in minutes between two backups. This is configurable only when `type` is `Periodic`. Possible values are between 60 and 1440. + +* `retention_in_hours` - (Optional) The time in hours that each backup is retained. This is configurable only when `type` is `Periodic`. Possible values are between 8 and 720. + ## Attributes Reference The following attributes are exported: