diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a57f825..dcb384f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * Add new change notes here FEATURES: * **New Resource:** sumologic_azure_metrics_source (GH-710) +* **New Resource:** sumologic_scan_budget ## 3.0.1 (January 17, 2025) **ENHANCEMENTS:** diff --git a/sumologic/provider.go b/sumologic/provider.go index 90b4e5ee..fbe9eebb 100644 --- a/sumologic/provider.go +++ b/sumologic/provider.go @@ -120,6 +120,7 @@ func Provider() terraform.ResourceProvider { "sumologic_role_v2": resourceSumologicRoleV2(), "sumologic_azure_event_hub_log_source": resourceSumologicGenericPollingSource(), "sumologic_azure_metrics_source": resourceSumologicGenericPollingSource(), + "sumologic_scan_budget": resourceSumologicScanBudget(), }, DataSourcesMap: map[string]*schema.Resource{ "sumologic_cse_log_mapping_vendor_product": dataSourceCSELogMappingVendorAndProduct(), diff --git a/sumologic/resource_sumologic_scan_budget.go b/sumologic/resource_sumologic_scan_budget.go new file mode 100644 index 00000000..a849606d --- /dev/null +++ b/sumologic/resource_sumologic_scan_budget.go @@ -0,0 +1,253 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Sumo Logic and manual +// changes will be clobbered when the file is regenerated. Do not submit +// changes to this file. +// +// ---------------------------------------------------------------------------- +package sumologic + +import ( + "log" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func resourceSumologicScanBudget() *schema.Resource { + return &schema.Resource{ + Create: resourceSumologicScanBudgetCreate, + Read: resourceSumologicScanBudgetRead, + Update: resourceSumologicScanBudgetUpdate, + Delete: resourceSumologicScanBudgetDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "capacity": { + Type: schema.TypeInt, + Required: true, + ForceNew: false, + }, + + "unit": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "budget_type": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "window": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "group_by": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "applicable_on": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "scope": { + Type: schema.TypeList, + Required: true, + ForceNew: false, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "included_users": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "excluded_users": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "included_roles": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "excluded_roles": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + + "action": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + + "status": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + }, + } +} + +func resourceSumologicScanBudgetCreate(d *schema.ResourceData, meta interface{}) error { + c := meta.(*Client) + + if d.Id() == "" { + scanBudget := resourceToScanBudget(d) + id, err := c.CreateScanBudget(scanBudget) + if err != nil { + return err + } + + d.SetId(id) + } + + return resourceSumologicScanBudgetRead(d, meta) +} + +func resourceSumologicScanBudgetRead(d *schema.ResourceData, meta interface{}) error { + c := meta.(*Client) + + id := d.Id() + scanBudget, err := c.GetScanBudget(id) + if err != nil { + return err + } + + if scanBudget == nil { + log.Printf("[WARN] ScanBudget not found, removing from state: %v - %v", id, err) + d.SetId("") + return nil + } + + d.Set("name", scanBudget.Name) + d.Set("capacity", scanBudget.Capacity) + d.Set("unit", scanBudget.Unit) + d.Set("budget_type", scanBudget.BudgetType) + d.Set("window", scanBudget.Window) + d.Set("applicable_on", scanBudget.ApplicableOn) + d.Set("group_by", scanBudget.GroupBy) + d.Set("action", scanBudget.Action) + d.Set("scope", scanBudgetScopeToResource(scanBudget.Scope)) + d.Set("status", scanBudget.Status) + + return nil +} + +func resourceSumologicScanBudgetDelete(d *schema.ResourceData, meta interface{}) error { + c := meta.(*Client) + + return c.DeleteScanBudget(d.Id()) +} + +func resourceSumologicScanBudgetUpdate(d *schema.ResourceData, meta interface{}) error { + c := meta.(*Client) + + scanBudget := resourceToScanBudget(d) + err := c.UpdateScanBudget(scanBudget) + if err != nil { + return err + } + + return resourceSumologicScanBudgetRead(d, meta) +} + +func resourceToScanBudget(d *schema.ResourceData) ScanBudget { + return ScanBudget{ + ID: d.Id(), + Name: d.Get("name").(string), + Capacity: d.Get("capacity").(int), + Unit: d.Get("unit").(string), + BudgetType: d.Get("budget_type").(string), + Window: d.Get("window").(string), + ApplicableOn: d.Get("applicable_on").(string), + GroupBy: d.Get("group_by").(string), + Action: d.Get("action").(string), + Scope: resourceToScanBudgetScope(d.Get("scope")), + Status: d.Get("status").(string), + } +} + +func resourceToScanBudgetScope(data interface{}) ScanBudgetScope { + scopeMap, ok := data.([]interface{})[0].(map[string]interface{}) + if !ok { + return ScanBudgetScope{} + } + + scanBudgetScope := ScanBudgetScope{ + IncludedUsers: convertToStringSlice(scopeMap["included_users"]), + ExcludedUsers: convertToStringSlice(scopeMap["excluded_users"]), + IncludedRoles: convertToStringSlice(scopeMap["included_roles"]), + ExcludedRoles: convertToStringSlice(scopeMap["excluded_roles"]), + } + + return scanBudgetScope +} + +func scanBudgetScopeToResource(scanBudgetScope ScanBudgetScope) []map[string]interface{} { + return []map[string]interface{}{ + { + "included_users": scanBudgetScope.IncludedUsers, + "excluded_users": scanBudgetScope.ExcludedUsers, + "included_roles": scanBudgetScope.IncludedRoles, + "excluded_roles": scanBudgetScope.ExcludedRoles, + }, + } +} + +func convertToStringSlice(data interface{}) []string { + if data == nil { + return nil + } + + interfaceSlice, ok := data.([]interface{}) + if !ok { + return nil + } + + stringSlice := make([]string, len(interfaceSlice)) + for i, v := range interfaceSlice { + str, ok := v.(string) + if ok { + stringSlice[i] = str + } + } + + return stringSlice +} diff --git a/sumologic/resource_sumologic_scan_budget_test.go b/sumologic/resource_sumologic_scan_budget_test.go new file mode 100644 index 00000000..9da3ee48 --- /dev/null +++ b/sumologic/resource_sumologic_scan_budget_test.go @@ -0,0 +1,295 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Sumo Logic and manual +// changes will be clobbered when the file is regenerated. Do not submit +// changes to this file. +// +// ---------------------------------------------------------------------------- +package sumologic + +import ( + "fmt" + "strconv" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccSumologicScanBudget_basic(t *testing.T) { + var scanBudget ScanBudget + + testName := "Test Budget" + testCapacity := 10 + testUnit := "GB" + testBudgetType := "ScanBudget" + testWindow := "Query" + testApplicableOn := "PerEntity" + testGroupBy := "User" + testAction := "StopForeGroundScan" + testScope := ScanBudgetScope{ + IncludedUsers: []string{"000000000000011C"}, + ExcludedUsers: []string{}, + IncludedRoles: []string{}, + ExcludedRoles: []string{"0000000000000196"}, + } + testStatus := "active" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckScanBudgetDestroy(scanBudget), + Steps: []resource.TestStep{ + { + Config: testAccCheckSumologicScanBudgetConfigImported(testName, testCapacity, testUnit, testBudgetType, testWindow, testApplicableOn, testGroupBy, testAction, testScope, testStatus), + }, + { + ResourceName: "sumologic_scan_budget.foo", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} +func TestAccSumologicScanBudget_create(t *testing.T) { + var scanBudget ScanBudget + + testName := "Test Budget" + testCapacity := 10 + testUnit := "GB" + testBudgetType := "ScanBudget" + testWindow := "Query" + testApplicableOn := "PerEntity" + testGroupBy := "User" + testAction := "StopForeGroundScan" + testScope := ScanBudgetScope{ + IncludedUsers: []string{"000000000000011C"}, + ExcludedUsers: []string{}, + IncludedRoles: []string{}, + ExcludedRoles: []string{"0000000000000196"}, + } + testStatus := "active" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckScanBudgetDestroy(scanBudget), + Steps: []resource.TestStep{ + { + Config: testAccSumologicScanBudget(testName, testCapacity, testUnit, testBudgetType, testWindow, testApplicableOn, testGroupBy, testAction, testScope, testStatus), + Check: resource.ComposeTestCheckFunc( + testAccCheckScanBudgetExists("sumologic_scan_budget.test", &scanBudget, t), + testAccCheckScanBudgetAttributes("sumologic_scan_budget.test"), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "name", testName), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "capacity", strconv.Itoa(testCapacity)), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "unit", testUnit), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "budget_type", testBudgetType), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "window", testWindow), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "applicable_on", testApplicableOn), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "group_by", testGroupBy), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "action", testAction), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "status", testStatus), + ), + }, + }, + }) +} + +func TestAccSumologicScanBudget_update(t *testing.T) { + var scanBudget ScanBudget + + testName := "Test Budget" + testCapacity := 10 + testUnit := "GB" + testBudgetType := "ScanBudget" + testWindow := "Query" + testApplicableOn := "PerEntity" + testGroupBy := "User" + testAction := "StopForeGroundScan" + testScope := ScanBudgetScope{ + IncludedUsers: []string{"000000000000011C"}, + ExcludedUsers: []string{}, + IncludedRoles: []string{}, + ExcludedRoles: []string{"0000000000000196"}, + } + testStatus := "active" + + testUpdatedName := "Test Budget" + testUpdatedCapacity := 20 + testUpdatedUnit := "GB" + testUpdatedBudgetType := "ScanBudget" + testUpdatedWindow := "Daily" + testUpdatedApplicableOn := "PerEntity" + testUpdatedGroupBy := "User" + testUpdatedAction := "Warn" + testUpdatedScope := ScanBudgetScope{ + IncludedUsers: []string{"000000000000011C"}, + ExcludedUsers: []string{}, + IncludedRoles: []string{}, + ExcludedRoles: []string{"0000000000000196"}, + } + testUpdatedStatus := "active" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckScanBudgetDestroy(scanBudget), + Steps: []resource.TestStep{ + { + Config: testAccSumologicScanBudget(testName, testCapacity, testUnit, testBudgetType, testWindow, testApplicableOn, testGroupBy, testAction, testScope, testStatus), + Check: resource.ComposeTestCheckFunc( + testAccCheckScanBudgetExists("sumologic_scan_budget.test", &scanBudget, t), + testAccCheckScanBudgetAttributes("sumologic_scan_budget.test"), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "name", testName), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "capacity", strconv.Itoa(testCapacity)), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "unit", testUnit), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "budget_type", testBudgetType), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "window", testWindow), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "applicable_on", testApplicableOn), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "group_by", testGroupBy), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "action", testAction), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "status", testStatus), + ), + }, + { + Config: testAccSumologicScanBudgetUpdate(testUpdatedName, testUpdatedCapacity, testUpdatedUnit, testUpdatedBudgetType, testUpdatedWindow, testUpdatedApplicableOn, testUpdatedGroupBy, testUpdatedAction, testUpdatedScope, testUpdatedStatus), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "name", testUpdatedName), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "capacity", strconv.Itoa(testUpdatedCapacity)), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "unit", testUpdatedUnit), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "budget_type", testUpdatedBudgetType), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "window", testUpdatedWindow), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "applicable_on", testUpdatedApplicableOn), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "group_by", testUpdatedGroupBy), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "action", testUpdatedAction), + resource.TestCheckResourceAttr("sumologic_scan_budget.test", "status", testUpdatedStatus), + ), + }, + }, + }) +} + +func testAccCheckScanBudgetDestroy(scanBudget ScanBudget) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*Client) + for _, r := range s.RootModule().Resources { + id := r.Primary.ID + _, err := client.GetScanBudget(id) + if err == nil { + return fmt.Errorf("ScanBudget %s still exists", id) + } + } + return nil + } +} + +func testAccCheckScanBudgetExists(name string, scanBudget *ScanBudget, t *testing.T) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + //need this so that we don't get an unused import error for strconv in some cases + return fmt.Errorf("Error = %s. ScanBudget not found: %s", strconv.FormatBool(ok), name) + } + + //need this so that we don't get an unused import error for strings in some cases + if strings.EqualFold(rs.Primary.ID, "") { + return fmt.Errorf("ScanBudget ID is not set") + } + + id := rs.Primary.ID + client := testAccProvider.Meta().(*Client) + newScanBudget, err := client.GetScanBudget(id) + if err != nil { + return fmt.Errorf("ScanBudget %s not found", id) + } + scanBudget = newScanBudget + return nil + } +} +func testAccCheckSumologicScanBudgetConfigImported(name string, capacity int, unit string, budgetType string, window string, applicableOn string, groupBy string, action string, scope ScanBudgetScope, status string) string { + return fmt.Sprintf(` +resource "sumologic_scan_budget" "foo" { + name = "%s" + capacity = %d + unit = "%s" + budget_type = "%s" + window = "%s" + applicable_on = "%s" + group_by = "%s" + action = "%s" + scope { + included_users = ["%s"] + excluded_users = [] + included_roles = [] + excluded_roles = ["%s"] + } + status = "%s" +} +`, name, capacity, unit, budgetType, window, applicableOn, groupBy, action, scope.IncludedUsers[0], scope.ExcludedRoles[0], status) +} + +func testAccSumologicScanBudget(name string, capacity int, unit string, budgetType string, window string, applicableOn string, groupBy string, action string, scope ScanBudgetScope, status string) string { + return fmt.Sprintf(` +resource "sumologic_scan_budget" "test" { + name = "%s" + capacity = %d + unit = "%s" + budget_type = "%s" + window = "%s" + applicable_on = "%s" + group_by = "%s" + action = "%s" + scope { + included_users = ["%s"] + excluded_users = [] + included_roles = [] + excluded_roles = ["%s"] + } + status = "%s" +} +`, name, capacity, unit, budgetType, window, applicableOn, groupBy, action, scope.IncludedUsers[0], scope.ExcludedRoles[0], status) +} + +func testAccSumologicScanBudgetUpdate(name string, capacity int, unit string, budgetType string, window string, applicableOn string, groupBy string, action string, scope ScanBudgetScope, status string) string { + return fmt.Sprintf(` +resource "sumologic_scan_budget" "test" { + name = "%s" + capacity = %d + unit = "%s" + budget_type = "%s" + window = "%s" + applicable_on = "%s" + group_by = "%s" + action = "%s" + scope { + included_users = ["%s"] + excluded_users = [] + included_roles = [] + excluded_roles = ["%s"] + } + status = "%s" +} +`, name, capacity, unit, budgetType, window, applicableOn, groupBy, action, scope.IncludedUsers[0], scope.ExcludedRoles[0], status) +} + +func testAccCheckScanBudgetAttributes(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + f := resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet(name, "name"), + resource.TestCheckResourceAttrSet(name, "capacity"), + resource.TestCheckResourceAttrSet(name, "unit"), + resource.TestCheckResourceAttrSet(name, "budget_type"), + resource.TestCheckResourceAttrSet(name, "window"), + resource.TestCheckResourceAttrSet(name, "applicable_on"), + resource.TestCheckResourceAttrSet(name, "group_by"), + resource.TestCheckResourceAttrSet(name, "action"), + resource.TestCheckResourceAttrSet(name, "status"), + ) + return f(s) + } +} diff --git a/sumologic/sumologic_scan_budget.go b/sumologic/sumologic_scan_budget.go new file mode 100644 index 00000000..e47da7d5 --- /dev/null +++ b/sumologic/sumologic_scan_budget.go @@ -0,0 +1,112 @@ +// ---------------------------------------------------------------------------- +// +// *** AUTO GENERATED CODE *** AUTO GENERATED CODE *** +// +// ---------------------------------------------------------------------------- +// +// This file is automatically generated by Sumo Logic and manual +// changes will be clobbered when the file is regenerated. Do not submit +// changes to this file. +// +// ---------------------------------------------------------------------------- +package sumologic + +import ( + "encoding/json" + "fmt" +) + +func (s *Client) CreateScanBudget(scanBudget ScanBudget) (string, error) { + urlWithoutParams := "v1/budgets" + + data, err := s.Post(urlWithoutParams, scanBudget) + if err != nil { + return "", err + } + + var createdScanBudget ScanBudget + + err = json.Unmarshal(data, &createdScanBudget) + if err != nil { + return "", err + } + + return createdScanBudget.ID, nil +} + +func (s *Client) GetScanBudget(id string) (*ScanBudget, error) { + urlWithoutParams := "v1/budgets/%s" + paramString := "" + sprintfArgs := []interface{}{} + sprintfArgs = append(sprintfArgs, id) + + urlWithParams := fmt.Sprintf(urlWithoutParams+paramString, sprintfArgs...) + + data, _, err := s.Get(urlWithParams) + if err != nil { + return nil, err + } + if data == nil { + return nil, nil + } + + var scanBudget ScanBudget + + err = json.Unmarshal(data, &scanBudget) + if err != nil { + return nil, err + } + + return &scanBudget, nil + +} + +func (s *Client) DeleteScanBudget(id string) error { + urlWithoutParams := "v1/budgets/%s" + paramString := "" + sprintfArgs := []interface{}{} + sprintfArgs = append(sprintfArgs, id) + + urlWithParams := fmt.Sprintf(urlWithoutParams+paramString, sprintfArgs...) + + _, err := s.Delete(urlWithParams) + + return err +} + +func (s *Client) UpdateScanBudget(scanBudget ScanBudget) error { + urlWithoutParams := "v1/budgets/%s" + paramString := "" + sprintfArgs := []interface{}{} + sprintfArgs = append(sprintfArgs, scanBudget.ID) + + urlWithParams := fmt.Sprintf(urlWithoutParams+paramString, sprintfArgs...) + + scanBudget.ID = "" + + _, err := s.Put(urlWithParams, scanBudget) + + return err + +} + +type ScanBudget struct { + ID string `json:"id,omitempty"` + Name string `json:"name"` + Capacity int `json:"capacity"` + Unit string `json:"unit"` + BudgetType string `json:"budgetType,omitempty"` + Window string `json:"window"` + ApplicableOn string `json:"applicableOn"` + GroupBy string `json:"groupBy"` + Action string `json:"action"` + Scope ScanBudgetScope `json:"scope"` + Status string `json:"status"` +} + +type ScanBudgetScope struct { + IncludedUsers []string `json:"includedUsers,omitempty"` + ExcludedUsers []string `json:"excludedUsers,omitempty"` + IncludedRoles []string `json:"includedRoles,omitempty"` + ExcludedRoles []string `json:"excludedRoles,omitempty"` +} diff --git a/website/docs/r/scan_budget.html.markdown b/website/docs/r/scan_budget.html.markdown new file mode 100644 index 00000000..26be98d9 --- /dev/null +++ b/website/docs/r/scan_budget.html.markdown @@ -0,0 +1,58 @@ +--- +layout: "sumologic" +page_title: "SumoLogic: sumologic_scan_budget" +description: |- + Provides a Sumologic Scan Budget +--- + +# sumologic_scan_budget +Provides a [Sumologic Scan Budget][1]. + +## Example Usage +```hcl +resource "sumologic_scan_budget" "budget" { + name = "TestBudget" + capacity = 10 + unit = "GB" + budget_type = "ScanBudget" + window = "Query" + applicable_on = "PerEntity" + group_by = "User" + action = "StopScan" + status = "active" + scope { + included_users = ["000000000000011C"] + excluded_users = [] + included_roles = [] + excluded_roles = ["0000000000000196"] + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Display name of the scan budget. This must be unique across all the scan budgets. +* `capacity` - (Required) Capacity of the scan budget. Only whole numbers are supported. +* `unit` - (Required) Unit of the capacity. Supported values are: `MB`, `GB` and `TB`. +* `budget_type` - (Required) Type of the budget. Supported values are: `ScanBudget`. +* `window` - (Required) Window of the budget. Supported values are: `Query`, `Daily`, `Weekly` and `Monthly`. +* `applicable_on` - (Required) Grouping of the budget. Supported values are: `PerEntity` and `Sum`. +* `group_by` - (Required) Grouping Entity of the budget. Supported values are: `User`. +* `action` - (Required) Action to be taken if the budget is breached. Supported values are: `StopForeGroundScan` and `Warn`. +* `scope` - (Required) Scope of the budget. +* `status` - (Required) Signifies the state of the budget. Supported values are: `active` and `inactive`. + +The following attributes are exported: + +* `id` - The internal ID of the budget. + +## Import +Scan budgets can be imported using the budget ID, e.g.: + +```hcl +terraform import sumologic_scan_budget.budget 00000000000123AB +``` + +[1]: https://help.sumologic.com/docs/manage/manage-subscription/usage-management/