From d54270c0bd3b14d8637749e8c7f32aa88e7ecf2e Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sun, 3 May 2020 17:22:49 +0300 Subject: [PATCH 1/7] add plan time validations: `budget_type`, `time_unit`, `subscriber_sns_topic_arns` add arn attribute --- aws/resource_aws_budgets_budget.go | 36 ++++++- aws/resource_aws_budgets_budget_test.go | 120 ++++++++++++------------ 2 files changed, 95 insertions(+), 61 deletions(-) diff --git a/aws/resource_aws_budgets_budget.go b/aws/resource_aws_budgets_budget.go index 1f65e6d4678b..99ab6c402cc6 100644 --- a/aws/resource_aws_budgets_budget.go +++ b/aws/resource_aws_budgets_budget.go @@ -7,6 +7,7 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/budgets" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -16,6 +17,10 @@ import ( func resourceAwsBudgetsBudget() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, "account_id": { Type: schema.TypeString, Computed: true, @@ -39,6 +44,14 @@ func resourceAwsBudgetsBudget() *schema.Resource { "budget_type": { Type: schema.TypeString, Required: true, + ValidateFunc: validation.StringInSlice([]string{ + budgets.BudgetTypeCost, + budgets.BudgetTypeRiCoverage, + budgets.BudgetTypeRiUtilization, + budgets.BudgetTypeSavingsPlansCoverage, + budgets.BudgetTypeSavingsPlansUtilization, + budgets.BudgetTypeUsage, + }, false), }, "limit_amount": { Type: schema.TypeString, @@ -125,6 +138,12 @@ func resourceAwsBudgetsBudget() *schema.Resource { "time_unit": { Type: schema.TypeString, Required: true, + ValidateFunc: validation.StringInSlice([]string{ + budgets.TimeUnitAnnually, + budgets.TimeUnitDaily, + budgets.TimeUnitMonthly, + budgets.TimeUnitQuarterly, + }, false), }, "cost_filters": { Type: schema.TypeMap, @@ -174,7 +193,10 @@ func resourceAwsBudgetsBudget() *schema.Resource { "subscriber_sns_topic_arns": { Type: schema.TypeSet, Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateArn, + }, }, }, }, @@ -350,6 +372,14 @@ func resourceAwsBudgetsBudgetRead(d *schema.ResourceData, meta interface{}) erro d.Set("time_unit", budget.TimeUnit) + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Service: "budgetservice", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("budget/%s", aws.StringValue(budget.BudgetName)), + } + d.Set("arn", arn.String()) + return resourceAwsBudgetsBudgetNotificationRead(d, meta) } @@ -402,9 +432,9 @@ func resourceAwsBudgetsBudgetNotificationRead(d *schema.ResourceData, meta inter emailSubscribers := make([]interface{}, 0) for _, subscriberOutput := range subscribersOutput.Subscribers { - if *subscriberOutput.SubscriptionType == budgets.SubscriptionTypeSns { + if aws.StringValue(subscriberOutput.SubscriptionType) == budgets.SubscriptionTypeSns { snsSubscribers = append(snsSubscribers, *subscriberOutput.Address) - } else if *subscriberOutput.SubscriptionType == budgets.SubscriptionTypeEmail { + } else if aws.StringValue(subscriberOutput.SubscriptionType) == budgets.SubscriptionTypeEmail { emailSubscribers = append(emailSubscribers, *subscriberOutput.Address) } } diff --git a/aws/resource_aws_budgets_budget_test.go b/aws/resource_aws_budgets_budget_test.go index 80e0a13f41be..782eee6e3d94 100644 --- a/aws/resource_aws_budgets_budget_test.go +++ b/aws/resource_aws_budgets_budget_test.go @@ -78,10 +78,11 @@ func testSweepBudgetsBudgets(region string) error { func TestAccAWSBudgetsBudget_basic(t *testing.T) { costFilterKey := "AZ" - name := fmt.Sprintf("test-budget-%d", acctest.RandInt()) - configBasicDefaults := testAccAWSBudgetsBudgetConfigDefaults(name) + rName := acctest.RandomWithPrefix("tf-acc-test") + configBasicDefaults := testAccAWSBudgetsBudgetConfigDefaults(rName) accountID := "012345678910" - configBasicUpdate := testAccAWSBudgetsBudgetConfigUpdate(name) + configBasicUpdate := testAccAWSBudgetsBudgetConfigUpdate(rName) + resourceName := "aws_budgets_budget.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSBudgets(t) }, @@ -91,14 +92,15 @@ func TestAccAWSBudgetsBudget_basic(t *testing.T) { { Config: testAccAWSBudgetsBudgetConfig_BasicDefaults(configBasicDefaults, costFilterKey), Check: resource.ComposeTestCheckFunc( - testAccAWSBudgetsBudgetExists("aws_budgets_budget.foo", configBasicDefaults), - resource.TestMatchResourceAttr("aws_budgets_budget.foo", "name", regexp.MustCompile(*configBasicDefaults.BudgetName)), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "budget_type", *configBasicDefaults.BudgetType), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "limit_amount", *configBasicDefaults.BudgetLimit.Amount), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "limit_unit", *configBasicDefaults.BudgetLimit.Unit), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "time_period_start", configBasicDefaults.TimePeriod.Start.Format("2006-01-02_15:04")), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "time_period_end", configBasicDefaults.TimePeriod.End.Format("2006-01-02_15:04")), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "time_unit", *configBasicDefaults.TimeUnit), + testAccAWSBudgetsBudgetExists(resourceName, configBasicDefaults), + testAccMatchResourceAttrGlobalARN(resourceName, "arn", "budgetservice", regexp.MustCompile(`budget/.+`)), + resource.TestMatchResourceAttr(resourceName, "name", regexp.MustCompile(*configBasicDefaults.BudgetName)), + resource.TestCheckResourceAttr(resourceName, "budget_type", *configBasicDefaults.BudgetType), + resource.TestCheckResourceAttr(resourceName, "limit_amount", *configBasicDefaults.BudgetLimit.Amount), + resource.TestCheckResourceAttr(resourceName, "limit_unit", *configBasicDefaults.BudgetLimit.Unit), + resource.TestCheckResourceAttr(resourceName, "time_period_start", configBasicDefaults.TimePeriod.Start.Format("2006-01-02_15:04")), + resource.TestCheckResourceAttr(resourceName, "time_period_end", configBasicDefaults.TimePeriod.End.Format("2006-01-02_15:04")), + resource.TestCheckResourceAttr(resourceName, "time_unit", *configBasicDefaults.TimeUnit), ), }, { @@ -109,18 +111,18 @@ func TestAccAWSBudgetsBudget_basic(t *testing.T) { { Config: testAccAWSBudgetsBudgetConfig_Basic(configBasicUpdate, costFilterKey), Check: resource.ComposeTestCheckFunc( - testAccAWSBudgetsBudgetExists("aws_budgets_budget.foo", configBasicUpdate), - resource.TestMatchResourceAttr("aws_budgets_budget.foo", "name", regexp.MustCompile(*configBasicUpdate.BudgetName)), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "budget_type", *configBasicUpdate.BudgetType), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "limit_amount", *configBasicUpdate.BudgetLimit.Amount), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "limit_unit", *configBasicUpdate.BudgetLimit.Unit), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "time_period_start", configBasicUpdate.TimePeriod.Start.Format("2006-01-02_15:04")), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "time_period_end", configBasicUpdate.TimePeriod.End.Format("2006-01-02_15:04")), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "time_unit", *configBasicUpdate.TimeUnit), + testAccAWSBudgetsBudgetExists(resourceName, configBasicUpdate), + resource.TestMatchResourceAttr(resourceName, "name", regexp.MustCompile(*configBasicUpdate.BudgetName)), + resource.TestCheckResourceAttr(resourceName, "budget_type", *configBasicUpdate.BudgetType), + resource.TestCheckResourceAttr(resourceName, "limit_amount", *configBasicUpdate.BudgetLimit.Amount), + resource.TestCheckResourceAttr(resourceName, "limit_unit", *configBasicUpdate.BudgetLimit.Unit), + resource.TestCheckResourceAttr(resourceName, "time_period_start", configBasicUpdate.TimePeriod.Start.Format("2006-01-02_15:04")), + resource.TestCheckResourceAttr(resourceName, "time_period_end", configBasicUpdate.TimePeriod.End.Format("2006-01-02_15:04")), + resource.TestCheckResourceAttr(resourceName, "time_unit", *configBasicUpdate.TimeUnit), ), }, { - ResourceName: "aws_budgets_budget.foo", + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{"name_prefix"}, @@ -131,9 +133,10 @@ func TestAccAWSBudgetsBudget_basic(t *testing.T) { func TestAccAWSBudgetsBudget_prefix(t *testing.T) { costFilterKey := "AZ" - name := "test-budget-" - configBasicDefaults := testAccAWSBudgetsBudgetConfigDefaults(name) - configBasicUpdate := testAccAWSBudgetsBudgetConfigUpdate(name) + rName := acctest.RandomWithPrefix("tf-acc-test") + configBasicDefaults := testAccAWSBudgetsBudgetConfigDefaults(rName) + configBasicUpdate := testAccAWSBudgetsBudgetConfigUpdate(rName) + resourceName := "aws_budgets_budget.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSBudgets(t) }, @@ -143,33 +146,33 @@ func TestAccAWSBudgetsBudget_prefix(t *testing.T) { { Config: testAccAWSBudgetsBudgetConfig_PrefixDefaults(configBasicDefaults, costFilterKey), Check: resource.ComposeTestCheckFunc( - testAccAWSBudgetsBudgetExists("aws_budgets_budget.foo", configBasicDefaults), - resource.TestMatchResourceAttr("aws_budgets_budget.foo", "name_prefix", regexp.MustCompile(*configBasicDefaults.BudgetName)), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "budget_type", *configBasicDefaults.BudgetType), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "limit_amount", *configBasicDefaults.BudgetLimit.Amount), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "limit_unit", *configBasicDefaults.BudgetLimit.Unit), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "time_period_start", configBasicDefaults.TimePeriod.Start.Format("2006-01-02_15:04")), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "time_period_end", configBasicDefaults.TimePeriod.End.Format("2006-01-02_15:04")), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "time_unit", *configBasicDefaults.TimeUnit), + testAccAWSBudgetsBudgetExists(resourceName, configBasicDefaults), + resource.TestMatchResourceAttr(resourceName, "name_prefix", regexp.MustCompile(*configBasicDefaults.BudgetName)), + resource.TestCheckResourceAttr(resourceName, "budget_type", *configBasicDefaults.BudgetType), + resource.TestCheckResourceAttr(resourceName, "limit_amount", *configBasicDefaults.BudgetLimit.Amount), + resource.TestCheckResourceAttr(resourceName, "limit_unit", *configBasicDefaults.BudgetLimit.Unit), + resource.TestCheckResourceAttr(resourceName, "time_period_start", configBasicDefaults.TimePeriod.Start.Format("2006-01-02_15:04")), + resource.TestCheckResourceAttr(resourceName, "time_period_end", configBasicDefaults.TimePeriod.End.Format("2006-01-02_15:04")), + resource.TestCheckResourceAttr(resourceName, "time_unit", *configBasicDefaults.TimeUnit), ), }, { Config: testAccAWSBudgetsBudgetConfig_Prefix(configBasicUpdate, costFilterKey), Check: resource.ComposeTestCheckFunc( - testAccAWSBudgetsBudgetExists("aws_budgets_budget.foo", configBasicUpdate), - resource.TestMatchResourceAttr("aws_budgets_budget.foo", "name_prefix", regexp.MustCompile(*configBasicUpdate.BudgetName)), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "budget_type", *configBasicUpdate.BudgetType), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "limit_amount", *configBasicUpdate.BudgetLimit.Amount), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "limit_unit", *configBasicUpdate.BudgetLimit.Unit), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "time_period_start", configBasicUpdate.TimePeriod.Start.Format("2006-01-02_15:04")), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "time_period_end", configBasicUpdate.TimePeriod.End.Format("2006-01-02_15:04")), - resource.TestCheckResourceAttr("aws_budgets_budget.foo", "time_unit", *configBasicUpdate.TimeUnit), + testAccAWSBudgetsBudgetExists(resourceName, configBasicUpdate), + resource.TestMatchResourceAttr(resourceName, "name_prefix", regexp.MustCompile(*configBasicUpdate.BudgetName)), + resource.TestCheckResourceAttr(resourceName, "budget_type", *configBasicUpdate.BudgetType), + resource.TestCheckResourceAttr(resourceName, "limit_amount", *configBasicUpdate.BudgetLimit.Amount), + resource.TestCheckResourceAttr(resourceName, "limit_unit", *configBasicUpdate.BudgetLimit.Unit), + resource.TestCheckResourceAttr(resourceName, "time_period_start", configBasicUpdate.TimePeriod.Start.Format("2006-01-02_15:04")), + resource.TestCheckResourceAttr(resourceName, "time_period_end", configBasicUpdate.TimePeriod.End.Format("2006-01-02_15:04")), + resource.TestCheckResourceAttr(resourceName, "time_unit", *configBasicUpdate.TimeUnit), ), }, { - ResourceName: "aws_budgets_budget.foo", + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{"name_prefix"}, @@ -179,9 +182,10 @@ func TestAccAWSBudgetsBudget_prefix(t *testing.T) { } func TestAccAWSBudgetsBudget_notification(t *testing.T) { - name := fmt.Sprintf("test-budget-%d", acctest.RandInt()) - configBasicDefaults := testAccAWSBudgetsBudgetConfigDefaults(name) + rName := acctest.RandomWithPrefix("tf-acc-test") + configBasicDefaults := testAccAWSBudgetsBudgetConfigDefaults(rName) configBasicDefaults.CostFilters = map[string][]*string{} + resourceName := "aws_budgets_budget.test" notificationConfigDefaults := []budgets.Notification{testAccAWSBudgetsBudgetNotificationConfigDefaults()} notificationConfigUpdated := []budgets.Notification{testAccAWSBudgetsBudgetNotificationConfigUpdate()} @@ -191,7 +195,7 @@ func TestAccAWSBudgetsBudget_notification(t *testing.T) { } noEmails := []string{} - oneEmail := []string{"foo@example.com"} + oneEmail := []string{"test@example.com"} oneOtherEmail := []string{"bar@example.com"} twoEmails := []string{"bar@example.com", "baz@example.com"} noTopics := []string{} @@ -207,42 +211,42 @@ func TestAccAWSBudgetsBudget_notification(t *testing.T) { Config: testAccAWSBudgetsBudgetConfigWithNotification_Basic(configBasicDefaults, notificationConfigDefaults, noEmails, noTopics), ExpectError: regexp.MustCompile(`Notification must have at least one subscriber`), Check: resource.ComposeTestCheckFunc( - testAccAWSBudgetsBudgetExists("aws_budgets_budget.foo", configBasicDefaults), + testAccAWSBudgetsBudgetExists(resourceName, configBasicDefaults), ), }, // Basic Notification with only email { Config: testAccAWSBudgetsBudgetConfigWithNotification_Basic(configBasicDefaults, notificationConfigDefaults, oneEmail, noTopics), Check: resource.ComposeTestCheckFunc( - testAccAWSBudgetsBudgetExists("aws_budgets_budget.foo", configBasicDefaults), + testAccAWSBudgetsBudgetExists(resourceName, configBasicDefaults), ), }, // Change only subscriber to a different e-mail { Config: testAccAWSBudgetsBudgetConfigWithNotification_Basic(configBasicDefaults, notificationConfigDefaults, oneOtherEmail, noTopics), Check: resource.ComposeTestCheckFunc( - testAccAWSBudgetsBudgetExists("aws_budgets_budget.foo", configBasicDefaults), + testAccAWSBudgetsBudgetExists(resourceName, configBasicDefaults), ), }, // Add a second e-mail and a topic { Config: testAccAWSBudgetsBudgetConfigWithNotification_Basic(configBasicDefaults, notificationConfigDefaults, twoEmails, oneTopic), Check: resource.ComposeTestCheckFunc( - testAccAWSBudgetsBudgetExists("aws_budgets_budget.foo", configBasicDefaults), + testAccAWSBudgetsBudgetExists(resourceName, configBasicDefaults), ), }, // Delete both E-Mails { Config: testAccAWSBudgetsBudgetConfigWithNotification_Basic(configBasicDefaults, notificationConfigDefaults, noEmails, oneTopic), Check: resource.ComposeTestCheckFunc( - testAccAWSBudgetsBudgetExists("aws_budgets_budget.foo", configBasicDefaults), + testAccAWSBudgetsBudgetExists(resourceName, configBasicDefaults), ), }, // Swap one Topic fo one E-Mail { Config: testAccAWSBudgetsBudgetConfigWithNotification_Basic(configBasicDefaults, notificationConfigDefaults, oneEmail, noTopics), Check: resource.ComposeTestCheckFunc( - testAccAWSBudgetsBudgetExists("aws_budgets_budget.foo", configBasicDefaults), + testAccAWSBudgetsBudgetExists(resourceName, configBasicDefaults), ), }, // Can't update without at least one subscriber @@ -250,7 +254,7 @@ func TestAccAWSBudgetsBudget_notification(t *testing.T) { Config: testAccAWSBudgetsBudgetConfigWithNotification_Basic(configBasicDefaults, notificationConfigDefaults, noEmails, noTopics), ExpectError: regexp.MustCompile(`Notification must have at least one subscriber`), Check: resource.ComposeTestCheckFunc( - testAccAWSBudgetsBudgetExists("aws_budgets_budget.foo", configBasicDefaults), + testAccAWSBudgetsBudgetExists(resourceName, configBasicDefaults), ), }, // Update all non-subscription parameters @@ -258,7 +262,7 @@ func TestAccAWSBudgetsBudget_notification(t *testing.T) { Config: testAccAWSBudgetsBudgetConfigWithNotification_Basic(configBasicDefaults, notificationConfigUpdated, noEmails, noTopics), ExpectError: regexp.MustCompile(`Notification must have at least one subscriber`), Check: resource.ComposeTestCheckFunc( - testAccAWSBudgetsBudgetExists("aws_budgets_budget.foo", configBasicDefaults), + testAccAWSBudgetsBudgetExists(resourceName, configBasicDefaults), ), }, // Add a second subscription @@ -266,7 +270,7 @@ func TestAccAWSBudgetsBudget_notification(t *testing.T) { Config: testAccAWSBudgetsBudgetConfigWithNotification_Basic(configBasicDefaults, twoNotificationConfigs, noEmails, noTopics), ExpectError: regexp.MustCompile(`Notification must have at least one subscriber`), Check: resource.ComposeTestCheckFunc( - testAccAWSBudgetsBudgetExists("aws_budgets_budget.foo", configBasicDefaults), + testAccAWSBudgetsBudgetExists(resourceName, configBasicDefaults), ), }, }, @@ -506,7 +510,7 @@ func testAccAWSBudgetsBudgetConfig_WithAccountID(budgetConfig budgets.Budget, ac costFilterValue := *budgetConfig.CostFilters[costFilterKey][0] return fmt.Sprintf(` -resource "aws_budgets_budget" "foo" { +resource "aws_budgets_budget" "test" { account_id = "%s" name_prefix = "%s" budget_type = "%s" @@ -527,7 +531,7 @@ func testAccAWSBudgetsBudgetConfig_PrefixDefaults(budgetConfig budgets.Budget, c costFilterValue := *budgetConfig.CostFilters[costFilterKey][0] return fmt.Sprintf(` -resource "aws_budgets_budget" "foo" { +resource "aws_budgets_budget" "test" { name_prefix = "%s" budget_type = "%s" limit_amount = "%s" @@ -548,7 +552,7 @@ func testAccAWSBudgetsBudgetConfig_Prefix(budgetConfig budgets.Budget, costFilte costFilterValue := *budgetConfig.CostFilters[costFilterKey][0] return fmt.Sprintf(` -resource "aws_budgets_budget" "foo" { +resource "aws_budgets_budget" "test" { name_prefix = "%s" budget_type = "%s" limit_amount = "%s" @@ -575,7 +579,7 @@ func testAccAWSBudgetsBudgetConfig_BasicDefaults(budgetConfig budgets.Budget, co costFilterValue := *budgetConfig.CostFilters[costFilterKey][0] return fmt.Sprintf(` -resource "aws_budgets_budget" "foo" { +resource "aws_budgets_budget" "test" { name = "%s" budget_type = "%s" limit_amount = "%s" @@ -596,7 +600,7 @@ func testAccAWSBudgetsBudgetConfig_Basic(budgetConfig budgets.Budget, costFilter costFilterValue := *budgetConfig.CostFilters[costFilterKey][0] return fmt.Sprintf(` -resource "aws_budgets_budget" "foo" { +resource "aws_budgets_budget" "test" { name = "%s" budget_type = "%s" limit_amount = "%s" @@ -633,7 +637,7 @@ resource "aws_sns_topic" "budget_notifications" { name_prefix = "user-updates-topic" } -resource "aws_budgets_budget" "foo" { +resource "aws_budgets_budget" "test" { name = "%s" budget_type = "%s" limit_amount = "%s" From d41b006060c927f637e39fb87c23feb4752d983e Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Sun, 3 May 2020 17:25:07 +0300 Subject: [PATCH 2/7] add docs --- website/docs/r/budgets_budget.html.markdown | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/website/docs/r/budgets_budget.html.markdown b/website/docs/r/budgets_budget.html.markdown index 75ffbcb0f0d0..ecead1ce7096 100644 --- a/website/docs/r/budgets_budget.html.markdown +++ b/website/docs/r/budgets_budget.html.markdown @@ -123,22 +123,24 @@ The following arguments are supported: * `name` - (Optional) The name of a budget. Unique within accounts. * `name_prefix` - (Optional) The prefix of the name of a budget. Unique within accounts. * `budget_type` - (Required) Whether this budget tracks monetary cost or usage. -* `cost_filters` - (Optional) Map of [CostFilters](#CostFilters) key/value pairs to apply to the budget. -* `cost_types` - (Optional) Object containing [CostTypes](#CostTypes) The types of cost included in a budget, such as tax and subscriptions.. +* `cost_filters` - (Optional) Map of [Cost Filters](#Cost-Filters) key/value pairs to apply to the budget. +* `cost_types` - (Optional) Object containing [Cost Types](#Cost-Types) The types of cost included in a budget, such as tax and subscriptions.. * `limit_amount` - (Required) The amount of cost or usage being measured for a budget. * `limit_unit` - (Required) The unit of measurement used for the budget forecast, actual spend, or budget threshold, such as dollars or GB. See [Spend](http://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/data-type-spend.html) documentation. * `time_period_end` - (Optional) The end of the time period covered by the budget. There are no restrictions on the end date. Format: `2017-01-01_12:00`. * `time_period_start` - (Required) The start of the time period covered by the budget. The start date must come before the end date. Format: `2017-01-01_12:00`. -* `time_unit` - (Required) The length of time until a budget resets the actual and forecasted spend. Valid values: `MONTHLY`, `QUARTERLY`, `ANNUALLY`. -* `notification` - (Optional) Object containing [Budget Notifications](#BudgetNotification). Can be used multiple times to define more than one budget notification +* `time_unit` - (Required) The length of time until a budget resets the actual and forecasted spend. Valid values: `MONTHLY`, `QUARTERLY`, `ANNUALLY`, and `DAILY`. +* `notification` - (Optional) Object containing [Budget Notifications](#Budget-Notification). Can be used multiple times to define more than one budget notification ## Attributes Reference In addition to all arguments above, the following attributes are exported: * `id` - id of resource. +* `arn` - The ARN of the budget. -### CostTypes + +### Cost Types Valid keys for `cost_types` parameter. @@ -156,7 +158,7 @@ Valid keys for `cost_types` parameter. Refer to [AWS CostTypes documentation](https://docs.aws.amazon.com/aws-cost-management/latest/APIReference/API_budgets_CostTypes.html) for further detail. -### CostFilters +### Cost Filters Valid keys for `cost_filters` parameter vary depending on the `budget_type` value. @@ -177,7 +179,7 @@ Valid keys for `cost_filters` parameter vary depending on the `budget_type` valu Refer to [AWS CostFilter documentation](http://docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/data-type-filter.html) for further detail. -### BudgetNotification +### Budget Notification Valid keys for `notification` parameter. From 7b125fa8d963f72ea4ce3e6dd50fbd850bff7aba Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Wed, 6 May 2020 13:14:35 +0300 Subject: [PATCH 3/7] added disappears test case --- aws/resource_aws_budgets_budget_test.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/aws/resource_aws_budgets_budget_test.go b/aws/resource_aws_budgets_budget_test.go index 782eee6e3d94..4ca795c97c9e 100644 --- a/aws/resource_aws_budgets_budget_test.go +++ b/aws/resource_aws_budgets_budget_test.go @@ -277,6 +277,29 @@ func TestAccAWSBudgetsBudget_notification(t *testing.T) { }) } +func TestAccAWSBudgetsBudget_disappears(t *testing.T) { + costFilterKey := "AZ" + rName := acctest.RandomWithPrefix("tf-acc-test") + configBasicDefaults := testAccAWSBudgetsBudgetConfigDefaults(rName) + resourceName := "aws_budgets_budget.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSBudgets(t) }, + Providers: testAccProviders, + CheckDestroy: testAccAWSBudgetsBudgetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSBudgetsBudgetConfig_BasicDefaults(configBasicDefaults, costFilterKey), + Check: resource.ComposeTestCheckFunc( + testAccAWSBudgetsBudgetExists(resourceName, configBasicDefaults), + testAccCheckResourceDisappears(testAccProvider, resourceAwsBudgetsBudget(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func testAccAWSBudgetsBudgetExists(resourceName string, config budgets.Budget) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] From b315bb110dc3689df0d31384679b21c80e44b65e Mon Sep 17 00:00:00 2001 From: Ilia Lazebnik Date: Thu, 18 Jun 2020 12:57:12 +0300 Subject: [PATCH 4/7] sdk wrapper --- aws/resource_aws_budgets_budget.go | 2 +- aws/resource_aws_budgets_budget_test.go | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_budgets_budget.go b/aws/resource_aws_budgets_budget.go index 99ab6c402cc6..4f424503fada 100644 --- a/aws/resource_aws_budgets_budget.go +++ b/aws/resource_aws_budgets_budget.go @@ -244,7 +244,7 @@ func resourceAwsBudgetsBudgetCreate(d *schema.ResourceData, meta interface{}) er return fmt.Errorf("create budget failed: %v", err) } - d.SetId(fmt.Sprintf("%s:%s", accountID, *budget.BudgetName)) + d.SetId(fmt.Sprintf("%s:%s", accountID, aws.StringValue(budget.BudgetName))) notificationsRaw := d.Get("notification").(*schema.Set).List() notifications, subscribers := expandBudgetNotificationsUnmarshal(notificationsRaw) diff --git a/aws/resource_aws_budgets_budget_test.go b/aws/resource_aws_budgets_budget_test.go index 4ca795c97c9e..af1346c17b3a 100644 --- a/aws/resource_aws_budgets_budget_test.go +++ b/aws/resource_aws_budgets_budget_test.go @@ -93,7 +93,7 @@ func TestAccAWSBudgetsBudget_basic(t *testing.T) { Config: testAccAWSBudgetsBudgetConfig_BasicDefaults(configBasicDefaults, costFilterKey), Check: resource.ComposeTestCheckFunc( testAccAWSBudgetsBudgetExists(resourceName, configBasicDefaults), - testAccMatchResourceAttrGlobalARN(resourceName, "arn", "budgetservice", regexp.MustCompile(`budget/.+`)), + testAccCheckResourceAttrGlobalARN(resourceName, "arn", "budgetservice", fmt.Sprintf(`budget/%s`, rName)), resource.TestMatchResourceAttr(resourceName, "name", regexp.MustCompile(*configBasicDefaults.BudgetName)), resource.TestCheckResourceAttr(resourceName, "budget_type", *configBasicDefaults.BudgetType), resource.TestCheckResourceAttr(resourceName, "limit_amount", *configBasicDefaults.BudgetLimit.Amount), @@ -326,8 +326,9 @@ func testAccAWSBudgetsBudgetExists(resourceName string, config budgets.Budget) r return fmt.Errorf("No budget returned %v in %v", b.Budget, b) } - if *b.Budget.BudgetLimit.Amount != *config.BudgetLimit.Amount { - return fmt.Errorf("budget limit incorrectly set %v != %v", *config.BudgetLimit.Amount, *b.Budget.BudgetLimit.Amount) + if aws.StringValue(b.Budget.BudgetLimit.Amount) != aws.StringValue(config.BudgetLimit.Amount) { + return fmt.Errorf("budget limit incorrectly set %v != %v", aws.StringValue(config.BudgetLimit.Amount), + aws.StringValue(b.Budget.BudgetLimit.Amount)) } if err := testAccAWSBudgetsBudgetCheckCostTypes(config, *b.Budget.CostTypes); err != nil { From 67cb1602000a1fc3b63c8beed4ee585bd90ffd4a Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Fri, 4 Sep 2020 14:35:09 +0300 Subject: [PATCH 5/7] Revert "resource/aws_storagegateway_cached_iscsi_volume: Add kms_encrypted and kms_key arguments (#12066)" This reverts commit eb63cfb2 --- ..._aws_storagegateway_cached_iscsi_volume.go | 28 +------------------ ...egateway_cached_iscsi_volume.html.markdown | 2 -- 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/aws/resource_aws_storagegateway_cached_iscsi_volume.go b/aws/resource_aws_storagegateway_cached_iscsi_volume.go index c56811ea6d5e..c35ed2901f92 100644 --- a/aws/resource_aws_storagegateway_cached_iscsi_volume.go +++ b/aws/resource_aws_storagegateway_cached_iscsi_volume.go @@ -87,18 +87,6 @@ func resourceAwsStorageGatewayCachedIscsiVolume() *schema.Resource { ForceNew: true, }, "tags": tagsSchema(), - "kms_encrypted": { - Type: schema.TypeBool, - Optional: true, - ForceNew: true, - }, - "kms_key": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validateArn, - RequiredWith: []string{"kms_encrypted"}, - }, }, } } @@ -123,14 +111,6 @@ func resourceAwsStorageGatewayCachedIscsiVolumeCreate(d *schema.ResourceData, me input.SourceVolumeARN = aws.String(v.(string)) } - if v, ok := d.GetOk("kms_key"); ok { - input.KMSKey = aws.String(v.(string)) - } - - if v, ok := d.GetOk("kms_encrypted"); ok { - input.KMSEncrypted = aws.Bool(v.(bool)) - } - log.Printf("[DEBUG] Creating Storage Gateway cached iSCSI volume: %s", input) output, err := conn.CreateCachediSCSIVolume(input) if err != nil { @@ -167,7 +147,7 @@ func resourceAwsStorageGatewayCachedIscsiVolumeRead(d *schema.ResourceData, meta output, err := conn.DescribeCachediSCSIVolumes(input) if err != nil { - if isAWSErr(err, storagegateway.ErrorCodeVolumeNotFound, "") || isAWSErr(err, storagegateway.ErrCodeInvalidGatewayRequestException, "The specified volume was not found") { + if isAWSErr(err, storagegateway.ErrorCodeVolumeNotFound, "") { log.Printf("[WARN] Storage Gateway cached iSCSI volume %q not found, removing from state", d.Id()) d.SetId("") return nil @@ -189,12 +169,6 @@ func resourceAwsStorageGatewayCachedIscsiVolumeRead(d *schema.ResourceData, meta d.Set("volume_arn", arn) d.Set("volume_id", aws.StringValue(volume.VolumeId)) d.Set("volume_size_in_bytes", int(aws.Int64Value(volume.VolumeSizeInBytes))) - d.Set("kms_key", volume.KMSKey) - if volume.KMSKey != nil { - d.Set("kms_encrypted", true) - } else { - d.Set("kms_encrypted", false) - } tags, err := keyvaluetags.StoragegatewayListTags(conn, arn) if err != nil { diff --git a/website/docs/r/storagegateway_cached_iscsi_volume.html.markdown b/website/docs/r/storagegateway_cached_iscsi_volume.html.markdown index da21068a4152..031f19701495 100644 --- a/website/docs/r/storagegateway_cached_iscsi_volume.html.markdown +++ b/website/docs/r/storagegateway_cached_iscsi_volume.html.markdown @@ -63,8 +63,6 @@ The following arguments are supported: * `volume_size_in_bytes` - (Required) The size of the volume in bytes. * `snapshot_id` - (Optional) The snapshot ID of the snapshot to restore as the new cached volume. e.g. `snap-1122aabb`. * `source_volume_arn` - (Optional) The ARN for an existing volume. Specifying this ARN makes the new volume into an exact copy of the specified existing volume's latest recovery point. The `volume_size_in_bytes` value for this new volume must be equal to or larger than the size of the existing volume, in bytes. -* `kms_encrypted` - (Optional) Set to `true` to use Amazon S3 server side encryption with your own AWS KMS key, or `false` to use a key managed by Amazon S3. -* `kms_key` - (Optional) The Amazon Resource Name (ARN) of the AWS KMS key used for Amazon S3 server side encryption. Is required when `kms_encrypted` is set. * `tags` - (Optional) Key-value map of resource tags ## Attribute Reference From 654ea3181457cd9638582b2f9e8f10874f05aca9 Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Fri, 4 Sep 2020 14:36:29 +0300 Subject: [PATCH 6/7] Revert "Revert "resource/aws_storagegateway_cached_iscsi_volume: Add kms_encrypted and kms_key arguments (#12066)"" This reverts commit 5ae123f4 --- ..._aws_storagegateway_cached_iscsi_volume.go | 28 ++++++++++++++++++- ...egateway_cached_iscsi_volume.html.markdown | 2 ++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_storagegateway_cached_iscsi_volume.go b/aws/resource_aws_storagegateway_cached_iscsi_volume.go index c35ed2901f92..c56811ea6d5e 100644 --- a/aws/resource_aws_storagegateway_cached_iscsi_volume.go +++ b/aws/resource_aws_storagegateway_cached_iscsi_volume.go @@ -87,6 +87,18 @@ func resourceAwsStorageGatewayCachedIscsiVolume() *schema.Resource { ForceNew: true, }, "tags": tagsSchema(), + "kms_encrypted": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + "kms_key": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateArn, + RequiredWith: []string{"kms_encrypted"}, + }, }, } } @@ -111,6 +123,14 @@ func resourceAwsStorageGatewayCachedIscsiVolumeCreate(d *schema.ResourceData, me input.SourceVolumeARN = aws.String(v.(string)) } + if v, ok := d.GetOk("kms_key"); ok { + input.KMSKey = aws.String(v.(string)) + } + + if v, ok := d.GetOk("kms_encrypted"); ok { + input.KMSEncrypted = aws.Bool(v.(bool)) + } + log.Printf("[DEBUG] Creating Storage Gateway cached iSCSI volume: %s", input) output, err := conn.CreateCachediSCSIVolume(input) if err != nil { @@ -147,7 +167,7 @@ func resourceAwsStorageGatewayCachedIscsiVolumeRead(d *schema.ResourceData, meta output, err := conn.DescribeCachediSCSIVolumes(input) if err != nil { - if isAWSErr(err, storagegateway.ErrorCodeVolumeNotFound, "") { + if isAWSErr(err, storagegateway.ErrorCodeVolumeNotFound, "") || isAWSErr(err, storagegateway.ErrCodeInvalidGatewayRequestException, "The specified volume was not found") { log.Printf("[WARN] Storage Gateway cached iSCSI volume %q not found, removing from state", d.Id()) d.SetId("") return nil @@ -169,6 +189,12 @@ func resourceAwsStorageGatewayCachedIscsiVolumeRead(d *schema.ResourceData, meta d.Set("volume_arn", arn) d.Set("volume_id", aws.StringValue(volume.VolumeId)) d.Set("volume_size_in_bytes", int(aws.Int64Value(volume.VolumeSizeInBytes))) + d.Set("kms_key", volume.KMSKey) + if volume.KMSKey != nil { + d.Set("kms_encrypted", true) + } else { + d.Set("kms_encrypted", false) + } tags, err := keyvaluetags.StoragegatewayListTags(conn, arn) if err != nil { diff --git a/website/docs/r/storagegateway_cached_iscsi_volume.html.markdown b/website/docs/r/storagegateway_cached_iscsi_volume.html.markdown index 031f19701495..da21068a4152 100644 --- a/website/docs/r/storagegateway_cached_iscsi_volume.html.markdown +++ b/website/docs/r/storagegateway_cached_iscsi_volume.html.markdown @@ -63,6 +63,8 @@ The following arguments are supported: * `volume_size_in_bytes` - (Required) The size of the volume in bytes. * `snapshot_id` - (Optional) The snapshot ID of the snapshot to restore as the new cached volume. e.g. `snap-1122aabb`. * `source_volume_arn` - (Optional) The ARN for an existing volume. Specifying this ARN makes the new volume into an exact copy of the specified existing volume's latest recovery point. The `volume_size_in_bytes` value for this new volume must be equal to or larger than the size of the existing volume, in bytes. +* `kms_encrypted` - (Optional) Set to `true` to use Amazon S3 server side encryption with your own AWS KMS key, or `false` to use a key managed by Amazon S3. +* `kms_key` - (Optional) The Amazon Resource Name (ARN) of the AWS KMS key used for Amazon S3 server side encryption. Is required when `kms_encrypted` is set. * `tags` - (Optional) Key-value map of resource tags ## Attribute Reference From 575df9f3e54cda45da8a0eefceb8bfd68f601a5d Mon Sep 17 00:00:00 2001 From: DrFaust92 Date: Fri, 4 Sep 2020 14:38:58 +0300 Subject: [PATCH 7/7] use enum slices --- aws/resource_aws_budgets_budget.go | 52 +++++++++--------------------- 1 file changed, 15 insertions(+), 37 deletions(-) diff --git a/aws/resource_aws_budgets_budget.go b/aws/resource_aws_budgets_budget.go index 4f424503fada..7979bae02c19 100644 --- a/aws/resource_aws_budgets_budget.go +++ b/aws/resource_aws_budgets_budget.go @@ -42,16 +42,9 @@ func resourceAwsBudgetsBudget() *schema.Resource { ForceNew: true, }, "budget_type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - budgets.BudgetTypeCost, - budgets.BudgetTypeRiCoverage, - budgets.BudgetTypeRiUtilization, - budgets.BudgetTypeSavingsPlansCoverage, - budgets.BudgetTypeSavingsPlansUtilization, - budgets.BudgetTypeUsage, - }, false), + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(budgets.BudgetType_Values(), false), }, "limit_amount": { Type: schema.TypeString, @@ -136,14 +129,9 @@ func resourceAwsBudgetsBudget() *schema.Resource { Default: "2087-06-15_00:00", }, "time_unit": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - budgets.TimeUnitAnnually, - budgets.TimeUnitDaily, - budgets.TimeUnitMonthly, - budgets.TimeUnitQuarterly, - }, false), + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(budgets.TimeUnit_Values(), false), }, "cost_filters": { Type: schema.TypeMap, @@ -157,33 +145,23 @@ func resourceAwsBudgetsBudget() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "comparison_operator": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - budgets.ComparisonOperatorEqualTo, - budgets.ComparisonOperatorGreaterThan, - budgets.ComparisonOperatorLessThan, - }, false), + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(budgets.ComparisonOperator_Values(), false), }, "threshold": { Type: schema.TypeFloat, Required: true, }, "threshold_type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - budgets.ThresholdTypeAbsoluteValue, - budgets.ThresholdTypePercentage, - }, false), + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(budgets.ThresholdType_Values(), false), }, "notification_type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - budgets.NotificationTypeActual, - budgets.NotificationTypeForecasted, - }, false), + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(budgets.NotificationType_Values(), false), }, "subscriber_email_addresses": { Type: schema.TypeSet,