diff --git a/azurerm/internal/services/securitycenter/azuresdkhacks/security_center_setting.go b/azurerm/internal/services/securitycenter/azuresdkhacks/security_center_setting.go new file mode 100644 index 0000000000000..f123befe795b1 --- /dev/null +++ b/azurerm/internal/services/securitycenter/azuresdkhacks/security_center_setting.go @@ -0,0 +1,36 @@ +package azuresdkhacks + +import ( + "context" + "fmt" + "net/http" + + "github.com/Azure/azure-sdk-for-go/services/preview/security/mgmt/v3.0/security" + "github.com/Azure/go-autorest/autorest" + "github.com/Azure/go-autorest/autorest/azure" +) + +func GetSecurityCenterSetting(client *security.SettingsClient, ctx context.Context, settingName string) (setting security.DataExportSettings, err error) { + // NOTE: client.Get() returns security.Setting, which doesn't contain the "Enabled" property + req, err := client.GetPreparer(ctx, settingName) + if err != nil { + err = autorest.NewErrorWithError(err, "security.SettingsClient", "Get", nil, "Failure preparing request") + return setting, fmt.Errorf("Error reading Security Center setting: %+v", err) + } + resp, err := client.GetSender(req) + if err != nil { + err = autorest.NewErrorWithError(err, "security.SettingsClient", "Get", resp, "Failure sending request") + return setting, fmt.Errorf("Error reading Security Center setting: %+v", err) + } + + err = autorest.Respond( + resp, + azure.WithErrorUnlessStatusCode(http.StatusOK), + autorest.ByUnmarshallingJSON(&setting), + autorest.ByClosing()) + if err != nil { + return setting, fmt.Errorf("Error reading Security Center setting: %+v", err) + } + + return setting, nil +} diff --git a/azurerm/internal/services/securitycenter/client/client.go b/azurerm/internal/services/securitycenter/client/client.go index 8b04c564792d3..daddbe00c0951 100644 --- a/azurerm/internal/services/securitycenter/client/client.go +++ b/azurerm/internal/services/securitycenter/client/client.go @@ -10,6 +10,7 @@ type Client struct { PricingClient *security.PricingsClient WorkspaceClient *security.WorkspaceSettingsClient AdvancedThreatProtectionClient *security.AdvancedThreatProtectionClient + SettingClient *security.SettingsClient } func NewClient(o *common.ClientOptions) *Client { @@ -27,10 +28,14 @@ func NewClient(o *common.ClientOptions) *Client { AdvancedThreatProtectionClient := security.NewAdvancedThreatProtectionClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId, ascLocation) o.ConfigureClient(&AdvancedThreatProtectionClient.Client, o.ResourceManagerAuthorizer) + SettingClient := security.NewSettingsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId, ascLocation) + o.ConfigureClient(&SettingClient.Client, o.ResourceManagerAuthorizer) + return &Client{ ContactsClient: &ContactsClient, PricingClient: &PricingClient, WorkspaceClient: &WorkspaceClient, AdvancedThreatProtectionClient: &AdvancedThreatProtectionClient, + SettingClient: &SettingClient, } } diff --git a/azurerm/internal/services/securitycenter/parse/security_center_setting.go b/azurerm/internal/services/securitycenter/parse/security_center_setting.go new file mode 100644 index 0000000000000..8a881e920a896 --- /dev/null +++ b/azurerm/internal/services/securitycenter/parse/security_center_setting.go @@ -0,0 +1,30 @@ +package parse + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +type SecurityCenterSettingId struct { + SettingName string +} + +func SecurityCenterSettingID(input string) (*SecurityCenterSettingId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, fmt.Errorf("Unable to parse Security Center Setting ID %q: %+v", input, err) + } + + setting := SecurityCenterSettingId{} + + if setting.SettingName, err = id.PopSegment("settings"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &setting, nil +} diff --git a/azurerm/internal/services/securitycenter/parse/security_center_setting_test.go b/azurerm/internal/services/securitycenter/parse/security_center_setting_test.go new file mode 100644 index 0000000000000..f3348a7d52bfe --- /dev/null +++ b/azurerm/internal/services/securitycenter/parse/security_center_setting_test.go @@ -0,0 +1,54 @@ +package parse + +import ( + "testing" +) + +func TestSecurityCenterSettingID(t *testing.T) { + testData := []struct { + Name string + Input string + Error bool + Expect *SecurityCenterSettingId + }{ + { + Name: "Empty", + Input: "", + Error: true, + }, + { + Name: "No Settings Segment", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000", + Error: true, + }, + { + Name: "No Settings Value", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/settings/", + Error: true, + }, + { + Name: "Security Center Setting ID", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/settings/MCAS", + Expect: &SecurityCenterSettingId{ + SettingName: "MCAS", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Name) + + actual, err := SecurityCenterSettingID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expected a value but got an error: %s", err) + } + + if actual.SettingName != v.Expect.SettingName { + t.Fatalf("Expected %q but got %q for Name", v.Expect.SettingName, actual.SettingName) + } + } +} diff --git a/azurerm/internal/services/securitycenter/registration.go b/azurerm/internal/services/securitycenter/registration.go index 46b02ab1016d4..cbf63cc4c3b57 100644 --- a/azurerm/internal/services/securitycenter/registration.go +++ b/azurerm/internal/services/securitycenter/registration.go @@ -28,6 +28,7 @@ func (r Registration) SupportedResources() map[string]*schema.Resource { return map[string]*schema.Resource{ "azurerm_advanced_threat_protection": resourceArmAdvancedThreatProtection(), "azurerm_security_center_contact": resourceArmSecurityCenterContact(), + "azurerm_security_center_setting": resourceArmSecurityCenterSetting(), "azurerm_security_center_subscription_pricing": resourceArmSecurityCenterSubscriptionPricing(), "azurerm_security_center_workspace": resourceArmSecurityCenterWorkspace(), } diff --git a/azurerm/internal/services/securitycenter/resource_arm_security_center_setting.go b/azurerm/internal/services/securitycenter/resource_arm_security_center_setting.go new file mode 100644 index 0000000000000..e939e67868c76 --- /dev/null +++ b/azurerm/internal/services/securitycenter/resource_arm_security_center_setting.go @@ -0,0 +1,112 @@ +package securitycenter + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/preview/security/mgmt/v3.0/security" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/securitycenter/azuresdkhacks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/securitycenter/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" +) + +func resourceArmSecurityCenterSetting() *schema.Resource { + return &schema.Resource{ + Create: resourceArmSecurityCenterSettingUpdate, + Read: resourceArmSecurityCenterSettingRead, + Update: resourceArmSecurityCenterSettingUpdate, + Delete: resourceArmSecurityCenterSettingDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(60 * time.Minute), + Read: schema.DefaultTimeout(5 * time.Minute), + Update: schema.DefaultTimeout(60 * time.Minute), + Delete: schema.DefaultTimeout(60 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "setting_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "MCAS", + "WDATP", + }, false), + }, + "enabled": { + Type: schema.TypeBool, + Required: true, + }, + }, + } +} + +func resourceArmSecurityCenterSettingUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).SecurityCenter.SettingClient + ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + settingName := d.Get("setting_name").(string) + enabled := d.Get("enabled").(bool) + setting := security.DataExportSettings{ + DataExportSettingProperties: &security.DataExportSettingProperties{ + Enabled: &enabled, + }, + Kind: security.KindDataExportSettings, + } + + if _, err := client.Update(ctx, settingName, setting); err != nil { + return fmt.Errorf("Error creating/updating Security Center pricing: %+v", err) + } + // TODO: switch to back when Swagger/API bug has been fixed: + // https://github.com/Azure/azure-sdk-for-go/issues/12687 + resp, err := azuresdkhacks.GetSecurityCenterSetting(client, ctx, settingName) + if err != nil { + return fmt.Errorf("Error reading Security Center setting: %+v", err) + } + + d.SetId(*resp.ID) + + return resourceArmSecurityCenterSettingRead(d, meta) +} + +func resourceArmSecurityCenterSettingRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).SecurityCenter.SettingClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.SecurityCenterSettingID(d.Id()) + if err != nil { + return err + } + // TODO: switch to back when Swagger/API bug has been fixed: + // https://github.com/Azure/azure-sdk-for-go/issues/12687 (`Enabled` field missing) + resp, err := azuresdkhacks.GetSecurityCenterSetting(client, ctx, id.SettingName) + if err != nil { + return fmt.Errorf("Error reading Security Center setting: %+v", err) + } + + if err != nil { + return fmt.Errorf("Error reading Security Center setting: %+v", err) + } + + if properties := resp.DataExportSettingProperties; properties != nil { + d.Set("enabled", properties.Enabled) + } + d.Set("setting_name", id.SettingName) + + return nil +} + +func resourceArmSecurityCenterSettingDelete(_ *schema.ResourceData, _ interface{}) error { + log.Printf("[DEBUG] Security Center deletion invocation") + return nil // cannot be deleted. +} diff --git a/azurerm/internal/services/securitycenter/tests/resource_arm_security_center_setting_test.go b/azurerm/internal/services/securitycenter/tests/resource_arm_security_center_setting_test.go new file mode 100644 index 0000000000000..3dad0a8f24e70 --- /dev/null +++ b/azurerm/internal/services/securitycenter/tests/resource_arm_security_center_setting_test.go @@ -0,0 +1,98 @@ +package tests + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMSecurityCenterSetting_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_security_center_setting", "test") + + // lintignore:AT001 + resource.Test(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMSecurityCenterSetting_cfg("MCAS", true), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSecurityCenterSettingExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "setting_name", "MCAS"), + resource.TestCheckResourceAttr(data.ResourceName, "enabled", "true"), + ), + }, + data.ImportStep(), + { + Config: testAccAzureRMSecurityCenterSetting_cfg("MCAS", false), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSecurityCenterSettingExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "setting_name", "MCAS"), + resource.TestCheckResourceAttr(data.ResourceName, "enabled", "false"), + ), + }, + data.ImportStep(), + { + Config: testAccAzureRMSecurityCenterSetting_cfg("WDATP", true), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSecurityCenterSettingExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "setting_name", "WDATP"), + resource.TestCheckResourceAttr(data.ResourceName, "enabled", "true"), + ), + }, + data.ImportStep(), + { + Config: testAccAzureRMSecurityCenterSetting_cfg("WDATP", false), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSecurityCenterSettingExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "setting_name", "WDATP"), + resource.TestCheckResourceAttr(data.ResourceName, "enabled", "false"), + ), + }, + data.ImportStep(), + }, + }) +} + +func testCheckAzureRMSecurityCenterSettingExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := acceptance.AzureProvider.Meta().(*clients.Client).SecurityCenter.SettingClient + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + settingName := rs.Primary.Attributes["setting_name"] + + resp, err := client.Get(ctx, settingName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Security Center Setting %q was not found: %+v", settingName, err) + } + + return fmt.Errorf("Bad: Get: %+v", err) + } + + return nil + } +} + +func testAccAzureRMSecurityCenterSetting_cfg(settingName string, enabled bool) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_security_center_setting" "example" { + setting_name = "%s" + enabled = "%t" +} +`, settingName, enabled) +} diff --git a/examples/security/securitycenter-setting/main.tf b/examples/security/securitycenter-setting/main.tf new file mode 100644 index 0000000000000..996f31b47d939 --- /dev/null +++ b/examples/security/securitycenter-setting/main.tf @@ -0,0 +1,8 @@ +provider "azurerm" { + features {} +} + +resource "azurerm_security_center_setting" "example" { + setting_name = "MCAS" + enabled = true +} \ No newline at end of file diff --git a/website/azurerm.erb b/website/azurerm.erb index c982087cad5af..dc703b4235714 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -2626,6 +2626,10 @@ azurerm_security_center_contact +
  • + azurerm_security_center_setting +
  • +
  • azurerm_security_center_subscription_pricing
  • diff --git a/website/docs/r/security_center_setting.markdown b/website/docs/r/security_center_setting.markdown new file mode 100644 index 0000000000000..3f785339cb3c7 --- /dev/null +++ b/website/docs/r/security_center_setting.markdown @@ -0,0 +1,54 @@ +--- +subcategory: "Security Center" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_security_center_setting" +description: |- + Manages the Data Access Settings for Azure Security Center. +--- + +# azurerm_security_center_setting + +Manages the Data Access Settings for Azure Security Center. + +~> **NOTE:** This resource requires the `Owner` permission on the Subscription. + +~> **NOTE:** Deletion of this resource does not change or reset the data access settings + +## Example Usage + +```hcl +resource "azurerm_security_center_setting" "example" { + setting_name = "MCAS" + enabled = true +} +``` + +## Argument Reference + +The following arguments are supported: + +* `setting_name` - (Required) The setting to manage. Possible values are `MCAS` and `WDATP`. +* `enabled` - (Required) Boolean flag to enable/disable data access. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The subscription security center setting id. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 60 minutes) Used when creating the Security Center Setting. +* `update` - (Defaults to 60 minutes) Used when updating the Security Center Setting. +* `read` - (Defaults to 5 minutes) Used when retrieving the Security Center Setting. +* `delete` - (Defaults to 60 minutes) Used when deleting the Security Center Setting. + +## Import + +The setting can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_security_center_setting.example /subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Security/settings/ +```