diff --git a/internal/services/consumption/consumption_budget_subscription_data_source.go b/internal/services/consumption/consumption_budget_subscription_data_source.go new file mode 100644 index 000000000000..fdc3d8af1b3a --- /dev/null +++ b/internal/services/consumption/consumption_budget_subscription_data_source.go @@ -0,0 +1,254 @@ +package consumption + +import ( + "fmt" + "time" + + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/consumption/validate" + subscriptionParse "github.com/hashicorp/terraform-provider-azurerm/internal/services/subscription/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceArmConsumptionBudgetSubscriptionDataSource() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: resourceArmConsumptionBudgetSubscriptionDataSourceRead, + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.ConsumptionBudgetName(), + }, + + "subscription_id": { + Type: pluginsdk.TypeString, + Required: true, + }, + + "amount": { + Type: pluginsdk.TypeFloat, + Computed: true, + }, + + "filter": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "dimension": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "operator": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "values": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + }, + }, + }, + "tag": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "operator": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "values": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + }, + }, + }, + "not": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "dimension": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "operator": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "values": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + }, + }, + }, + "tag": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "operator": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "values": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + + "notification": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "enabled": { + Type: pluginsdk.TypeBool, + Computed: true, + }, + "threshold": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + "operator": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "contact_emails": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "contact_groups": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "contact_roles": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + }, + }, + }, + + "time_grain": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "time_period": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "start_date": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "end_date": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func resourceArmConsumptionBudgetSubscriptionDataSourceRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).Consumption.BudgetsClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + subscriptionId, err := subscriptionParse.SubscriptionID(d.Get("subscription_id").(string)) + if err != nil { + return err + } + + id := parse.NewConsumptionBudgetSubscriptionID(subscriptionId.SubscriptionID, d.Get("name").(string)) + d.SetId(id.ID()) + + resp, err := client.Get(ctx, subscriptionId.ID(), id.BudgetName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("making read request on %s: %+v", id, err) + } + + d.Set("name", resp.Name) + if resp.Amount != nil { + amount, _ := resp.Amount.Float64() + d.Set("amount", amount) + } + d.Set("time_grain", string(resp.TimeGrain)) + d.Set("time_period", FlattenConsumptionBudgetTimePeriod(resp.TimePeriod)) + d.Set("notification", FlattenConsumptionBudgetNotifications(resp.Notifications)) + d.Set("filter", FlattenConsumptionBudgetFilter(resp.Filter)) + + // The scope of a Subscription budget resource is the Subscription budget ID + d.Set("subscription_id", d.Get("subscription_id").(string)) + + return nil +} diff --git a/internal/services/consumption/consumption_budget_subscription_data_source_test.go b/internal/services/consumption/consumption_budget_subscription_data_source_test.go new file mode 100644 index 000000000000..649a5a68d23a --- /dev/null +++ b/internal/services/consumption/consumption_budget_subscription_data_source_test.go @@ -0,0 +1,51 @@ +package consumption_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type ConsumptionBudgetSubscriptionDataSource struct{} + +func TestAccDataSourceConsumptionBudgetSubscription_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_consumption_budget_subscription", "test") + r := ConsumptionBudgetSubscriptionDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("subscription_id").HasValue(data.Client().SubscriptionID), + check.That(data.ResourceName).Key("name").Exists(), + check.That(data.ResourceName).Key("amount").HasValue("1000"), + check.That(data.ResourceName).Key("time_grain").HasValue("Monthly"), + check.That(data.ResourceName).Key("time_period.#").Exists(), + check.That(data.ResourceName).Key("time_period.0.start_date").Exists(), + check.That(data.ResourceName).Key("filter.#").Exists(), + check.That(data.ResourceName).Key("filter.0.tag.0.name").HasValue("foo"), + check.That(data.ResourceName).Key("filter.0.tag.0.values.#").Exists(), + check.That(data.ResourceName).Key("notification.#").Exists(), + check.That(data.ResourceName).Key("notification.0.threshold").HasValue("90"), + check.That(data.ResourceName).Key("notification.0.operator").HasValue("EqualTo"), + check.That(data.ResourceName).Key("notification.0.enabled").Exists(), + check.That(data.ResourceName).Key("notification.0.contact_emails.0").HasValue("foo@example.com"), + check.That(data.ResourceName).Key("notification.0.contact_emails.1").HasValue("bar@example.com"), + ), + }, + }) +} + +func (d ConsumptionBudgetSubscriptionDataSource) basic(data acceptance.TestData) string { + config := ConsumptionBudgetSubscriptionResource{}.basic(data) + return fmt.Sprintf(` + %s + +data "azurerm_consumption_budget_subscription" "test" { + name = azurerm_consumption_budget_subscription.test.name + subscription_id = azurerm_consumption_budget_subscription.test.subscription_id +} +`, config) +} diff --git a/internal/services/consumption/registration.go b/internal/services/consumption/registration.go index e7c7a2e4d0b6..e425bece5af6 100644 --- a/internal/services/consumption/registration.go +++ b/internal/services/consumption/registration.go @@ -9,8 +9,9 @@ const ( // as the core logic for the Consumption Budget resources is generic and has been // extracted out of the specific Consumption Budget resources. These constants are // used when the generic Consumption Budget functions require a resource name. - consumptionBudgetResourceGroupName = "azurerm_consumption_budget_resource_group" - consumptionBudgetSubscriptionName = "azurerm_consumption_budget_subscription" + consumptionBudgetResourceGroupName = "azurerm_consumption_budget_resource_group" + consumptionBudgetSubscriptionName = "azurerm_consumption_budget_subscription" + consumptionBudgetSubscriptionDataSourceName = "azurerm_consumption_budget_subscription" ) type Registration struct{} @@ -29,7 +30,9 @@ func (r Registration) WebsiteCategories() []string { // SupportedDataSources returns the supported Data Sources supported by this Service func (r Registration) SupportedDataSources() map[string]*pluginsdk.Resource { - return map[string]*pluginsdk.Resource{} + return map[string]*pluginsdk.Resource{ + consumptionBudgetSubscriptionDataSourceName: resourceArmConsumptionBudgetSubscriptionDataSource(), + } } // SupportedResources returns the supported Resources supported by this Service diff --git a/website/docs/d/consumption_budget_subscription.html.markdown b/website/docs/d/consumption_budget_subscription.html.markdown new file mode 100644 index 000000000000..0d9d43c1c720 --- /dev/null +++ b/website/docs/d/consumption_budget_subscription.html.markdown @@ -0,0 +1,120 @@ +--- +subcategory: "Consumption" +layout: "azurerm" +page_title: "Azure Resource Manager: Data Source: azurerm_consumption_budget_subscription" +description: |- + Gets information about an existing Consumption Budget for a subscription. +--- + +# Data Source: azurerm_consumption_budget_subscription + +Use this data source to access information about an existing Consumption Budget for a specific subscription. + +## Example Usage + +```hcl +data "azurerm_consumption_budget_subscription" "example" { + name = "existing" + subscription_id = "/subscriptions/00000000-0000-0000-0000-000000000000/" +} + +output "id" { + value = data.azurerm_consumption_budget.example.id +} +``` + +## Arguments Reference + +The following arguments are supported: + +* `name` - (Required) The name of this Consumption Budget. + +* `subscription_id` - (Required) The ID of the subscription. + +## Attributes Reference + +In addition to the Arguments listed above - the following Attributes are exported: + +* `id` - The ID of the Consumption Budget. + +* `amount` - The total amount of cost to track with the budget. + +* `filter` - A `filter` block as defined below. + +* `notification` - A `notification` block as defined below. + +* `time_grain` - The time covered by a budget. + +* `time_period` - A `time_period` block as defined below. + +--- + +A `dimension` block exports the following: + +* `name` - The name of the column used for the filter. + +* `operator` - The operator to used for comparison. + +* `values` - A `values` block as defined below. + +--- + +A `filter` block exports the following: + +* `dimension` - A `dimension` block as defined below. + +* `not` - A `not` block as defined below. + +* `tag` - A `tag` block as defined below. + +-> **Note:** The order of multiple filter entries is not guaranteed to be consistent by the API. + +--- + +A `not` block exports the following: + +* `dimension` - A `dimension` block as defined above. + +* `tag` - A `tag` block as defined below. + +--- + +A `notification` block exports the following: + +* `contact_emails` - A list of email addresses to send the budget notification to when the threshold is exceeded. + +* `contact_groups` - A list of Action Group IDs to send the budget notification to when the threshold is exceeded. + +* `contact_roles` - A list of contact roles to send the budget notification to when the threshold is exceeded. + +* `enabled` - Whether the notification is enabled. + +* `operator` - The comparison operator for the notification. + +* `threshold` - Threshold value associated with the notification. + +-> **Note:** The order of multiple notification entries is not guaranteed to be consistent by the API. + +--- + +A `tag` block exports the following: + +* `name` - The name of the tag to use for the filter. + +* `operator` - The operator to used for comparison. + +* `values` - A list of values for the tag. + +--- + +A `time_period` block exports the following: + +* `end_date` - The end date for the budget. + +* `start_date` - The start date for the budget. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `read` - (Defaults to 5 minutes) Used when retrieving the Consumption Budget.