diff --git a/.changelog/28055.txt b/.changelog/28055.txt new file mode 100644 index 00000000000..ee1afea97dc --- /dev/null +++ b/.changelog/28055.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_ce_cost_category: Allow configuration of `effective_start` value +``` diff --git a/internal/service/ce/cost_category.go b/internal/service/ce/cost_category.go index 5c3cc21499b..3ef8ef6cd7b 100644 --- a/internal/service/ce/cost_category.go +++ b/internal/service/ce/cost_category.go @@ -48,6 +48,7 @@ func ResourceCostCategory() *schema.Resource { }, "effective_start": { Type: schema.TypeString, + Optional: true, Computed: true, }, "name": { @@ -389,6 +390,10 @@ func resourceCostCategoryCreate(ctx context.Context, d *schema.ResourceData, met input.SplitChargeRules = expandCostCategorySplitChargeRules(v.(*schema.Set).List()) } + if v, ok := d.GetOk("effective_start"); ok { + input.EffectiveStart = aws.String(v.(string)) + } + if len(tags) > 0 { input.ResourceTags = Tags(tags.IgnoreAWS()) } @@ -474,6 +479,10 @@ func resourceCostCategoryUpdate(ctx context.Context, d *schema.ResourceData, met input.SplitChargeRules = expandCostCategorySplitChargeRules(d.Get("split_charge_rule").(*schema.Set).List()) } + if d.HasChange("effective_start") { + input.EffectiveStart = aws.String(d.Get("effective_start").(string)) + } + _, err := conn.UpdateCostCategoryDefinitionWithContext(ctx, input) if err != nil { diff --git a/internal/service/ce/cost_category_test.go b/internal/service/ce/cost_category_test.go index f8a59453692..5ae5537a464 100644 --- a/internal/service/ce/cost_category_test.go +++ b/internal/service/ce/cost_category_test.go @@ -3,6 +3,7 @@ package ce_test import ( "context" "fmt" + "regexp" "testing" "github.com/aws/aws-sdk-go/service/costexplorer" @@ -31,6 +32,8 @@ func TestAccCECostCategory_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckCostCategoryExists(resourceName, &output), resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttrSet(resourceName, "effective_start"), + acctest.MatchResourceAttrGlobalARN(resourceName, "arn", "ce", regexp.MustCompile(`costcategory/.+$`)), ), }, { @@ -42,6 +45,42 @@ func TestAccCECostCategory_basic(t *testing.T) { }) } +func TestAccCECostCategory_effectiveStart(t *testing.T) { + var output costexplorer.CostCategory + resourceName := "aws_ce_cost_category.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckCostCategoryDestroy, + ErrorCheck: acctest.ErrorCheck(t, costexplorer.EndpointsID), + Steps: []resource.TestStep{ + { + Config: testAccCostCategoryConfig_effectiveStart(rName, "2022-11-01T00:00:00Z"), + Check: resource.ComposeTestCheckFunc( + testAccCheckCostCategoryExists(resourceName, &output), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "effective_start", "2022-11-01T00:00:00Z"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccCostCategoryConfig_effectiveStart(rName, "2022-10-01T00:00:00Z"), + Check: resource.ComposeTestCheckFunc( + testAccCheckCostCategoryExists(resourceName, &output), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "effective_start", "2022-10-01T00:00:00Z"), + ), + }, + }, + }) +} + func TestAccCECostCategory_disappears(t *testing.T) { var output costexplorer.CostCategory resourceName := "aws_ce_cost_category.test" @@ -471,3 +510,46 @@ resource "aws_ce_cost_category" "test" { } `, rName, tagKey1, tagValue1, tagKey2, tagValue2) } + +func testAccCostCategoryConfig_effectiveStart(rName, date string) string { + return fmt.Sprintf(` +resource "aws_ce_cost_category" "test" { + name = %[1]q + rule_version = "CostCategoryExpression.v1" + effective_start = %[2]q + rule { + value = "production" + rule { + dimension { + key = "LINKED_ACCOUNT_NAME" + values = ["-prod"] + match_options = ["ENDS_WITH"] + } + } + type = "REGULAR" + } + rule { + value = "staging" + rule { + dimension { + key = "LINKED_ACCOUNT_NAME" + values = ["-stg"] + match_options = ["ENDS_WITH"] + } + } + type = "REGULAR" + } + rule { + value = "testing" + rule { + dimension { + key = "LINKED_ACCOUNT_NAME" + values = ["-dev"] + match_options = ["ENDS_WITH"] + } + } + type = "REGULAR" + } +} +`, rName, date) +} diff --git a/website/docs/r/ce_cost_category.html.markdown b/website/docs/r/ce_cost_category.html.markdown index e0bd7040ef3..7f7d3a9f667 100644 --- a/website/docs/r/ce_cost_category.html.markdown +++ b/website/docs/r/ce_cost_category.html.markdown @@ -56,6 +56,7 @@ The following arguments are required: * `name` - (Required) Unique name for the Cost Category. * `rule` - (Required) Configuration block for the Cost Category rules used to categorize costs. See below. * `rule_version` - (Required) Rule schema version in this particular Cost Category. +* `effective_start`- (Optional) The Cost Category's effective start date. It can only be a billing start date (first day of the month). If the date isn't provided, it's the first day of the current month. Dates can't be before the previous twelve months, or in the future. For example `2022-11-01T00:00:00Z`. The following arguments are optional: @@ -120,7 +121,6 @@ In addition to all arguments above, the following attributes are exported: * `arn` - ARN of the cost category. * `effective_end` - Effective end data of your Cost Category. -* `effective_start` - Effective state data of your Cost Category. * `id` - Unique ID of the cost category. * `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block).