diff --git a/azurerm/config.go b/azurerm/config.go index f9295dc68a0e0..1f905f5ec002d 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -254,7 +254,8 @@ type ArmClient struct { searchServicesClient search.ServicesClient // Security Centre - securityCenterPricingClient security.PricingsClient + securityCenterPricingClient security.PricingsClient + securityCenterContactsClient security.ContactsClient // ServiceBus serviceBusQueuesClient servicebus.QueuesClient @@ -1025,6 +1026,10 @@ func (c *ArmClient) registerSecurityCenterClients(endpoint, subscriptionId, ascL securityCenterPricingClient := security.NewPricingsClientWithBaseURI(endpoint, subscriptionId, ascLocation) c.configureClient(&securityCenterPricingClient.Client, auth) c.securityCenterPricingClient = securityCenterPricingClient + + securityCenterContactsClient := security.NewContactsClientWithBaseURI(endpoint, subscriptionId, ascLocation) + c.configureClient(&securityCenterContactsClient.Client, auth) + c.securityCenterContactsClient = securityCenterContactsClient } func (c *ArmClient) registerServiceBusClients(endpoint, subscriptionId string, auth autorest.Authorizer) { diff --git a/azurerm/provider.go b/azurerm/provider.go index e9488ae50e13b..eb52799831b40 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -244,6 +244,7 @@ func Provider() terraform.ResourceProvider { "azurerm_route_table": resourceArmRouteTable(), "azurerm_search_service": resourceArmSearchService(), "azurerm_securitycenter_subscription_pricing": resourceArmSecurityCenterSubscriptionPricing(), + "azurerm_securitycenter_contact": resourceArmSecurityCenterContact(), "azurerm_servicebus_namespace": resourceArmServiceBusNamespace(), "azurerm_servicebus_namespace_authorization_rule": resourceArmServiceBusNamespaceAuthorizationRule(), "azurerm_servicebus_queue": resourceArmServiceBusQueue(), diff --git a/azurerm/resource_arm_securitycenter_contact.go b/azurerm/resource_arm_securitycenter_contact.go new file mode 100644 index 0000000000000..092f7cd13ca7b --- /dev/null +++ b/azurerm/resource_arm_securitycenter_contact.go @@ -0,0 +1,153 @@ +package azurerm + +import ( + "fmt" + "log" + "strings" + + "github.com/Azure/azure-sdk-for-go/services/preview/security/mgmt/2017-08-01-preview/security" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +//seems you can only set one contact: +// Invalid security contact name was provided - only 'defaultX' is allowed where X is an index +// Invalid security contact name 'default0' was provided. Expected 'default1' +// Message="Invalid security contact name 'default2' was provided. Expected 'default1'" + +func resourceArmSecurityCenterContact() *schema.Resource { + return &schema.Resource{ + Create: resourceArmSecurityCenterContactCreateUpdate, + Read: resourceArmSecurityCenterContactRead, + Update: resourceArmSecurityCenterContactCreateUpdate, + Delete: resourceArmSecurityCenterContactDelete, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "email": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: suppress.CaseDifference, + //todo validation + }, + + "phone": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: suppress.CaseDifference, + ValidateFunc: validation.NoZeroValues, + }, + + "alert_notifications": { + Type: schema.TypeBool, + Required: true, + }, + + "alerts_to_admins": { + Type: schema.TypeBool, + Required: true, + }, + }, + } +} + +func resourceArmSecurityCenterContactCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).securityCenterContactsClient + ctx := meta.(*ArmClient).StopContext + + contact := security.Contact{ + ContactProperties: &security.ContactProperties{ + Email: utils.String(d.Get("email").(string)), + Phone: utils.String(d.Get("phone").(string)), + }, + } + + if alertNotifications := d.Get("alert_notifications").(bool); alertNotifications { + contact.AlertNotifications = security.On + } else { + contact.AlertNotifications = security.Off + } + + if alertNotifications := d.Get("alerts_to_admins").(bool); alertNotifications { + contact.AlertsToAdmins = security.AlertsToAdminsOn + } else { + contact.AlertsToAdmins = security.AlertsToAdminsOff + } + + if d.IsNewResource() { + _, err := client.Create(ctx, "default1", contact) + if err != nil { + return fmt.Errorf("Error creating Security Center Contact: %+v", err) + } + + resp, err := client.Get(ctx, "default1") + if err != nil { + return fmt.Errorf("Error reading Security Center Contact: %+v", err) + } + if resp.ID == nil { + return fmt.Errorf("Security Center Contact ID is nil") + } + + d.SetId(*resp.ID) + } else { + _, err := client.Update(ctx, "default1", contact) + if err != nil { + return fmt.Errorf("Error updating Security Center Contact: %+v", err) + } + } + + return resourceArmSecurityCenterContactRead(d, meta) +} + +func resourceArmSecurityCenterContactRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).securityCenterContactsClient + ctx := meta.(*ArmClient).StopContext + + //id is in format of `/subscriptions/20ff7fc3-e762-44dd-bd96-b71116dcdc23/providers/Microsoft.Security/securityContacts/john` + //parseAzureResourceID doesn't support id without a resource group + bits := strings.Split(d.Id(), "/") + name := bits[len(bits)-1] + + resp, err := client.Get(ctx, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] Security Center Subscription Contact was not found: %v", err) + d.SetId("") + return nil + } + + return fmt.Errorf("Error reading Security Center Contact: %+v", err) + } + + if properties := resp.ContactProperties; properties != nil { + d.Set("email", properties.Email) + d.Set("phone", properties.Phone) + d.Set("alert_notifications", properties.AlertNotifications == security.On) + d.Set("alerts_to_admins", properties.AlertsToAdmins == security.AlertsToAdminsOn) + } + + return nil +} + +func resourceArmSecurityCenterContactDelete(_ *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).securityCenterContactsClient + ctx := meta.(*ArmClient).StopContext + + resp, err := client.Delete(ctx, "default1") + if err != nil { + if utils.ResponseWasNotFound(resp) { + log.Printf("[DEBUG] Security Center Subscription Contact was not found: %v", err) + return nil + } + + return fmt.Errorf("Error deleting Security Center Contact: %+v", err) + } + + return nil +} diff --git a/azurerm/resource_arm_securitycenter_contact_test.go b/azurerm/resource_arm_securitycenter_contact_test.go new file mode 100644 index 0000000000000..cfba6f9eb705f --- /dev/null +++ b/azurerm/resource_arm_securitycenter_contact_test.go @@ -0,0 +1,109 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func TestAccAzureRMSecurityCenterContact_basic(t *testing.T) { + resourceName := "azurerm_securitycenter_contact.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMSecurityCenterContact_template("email1@example.com", "+1-555-555-5555", true, true), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSecurityCenterContactExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "email", "email1@example.com"), + resource.TestCheckResourceAttr(resourceName, "phone", "+1-555-555-5555"), + resource.TestCheckResourceAttr(resourceName, "alert_notifications", "true"), + resource.TestCheckResourceAttr(resourceName, "alerts_to_admins", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMSecurityCenterContact_update(t *testing.T) { + resourceName := "azurerm_securitycenter_contact.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMSecurityCenterContact_template("email1@example.com", "+1-555-555-5555", true, true), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSecurityCenterContactExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "email", "email1@example.com"), + resource.TestCheckResourceAttr(resourceName, "phone", "+1-555-555-5555"), + resource.TestCheckResourceAttr(resourceName, "alert_notifications", "true"), + resource.TestCheckResourceAttr(resourceName, "alerts_to_admins", "true"), + ), + }, + { + Config: testAccAzureRMSecurityCenterContact_template("email2@example.com", "+1-555-678-6789", false, false), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMSecurityCenterContactExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "email", "email2@example.com"), + resource.TestCheckResourceAttr(resourceName, "phone", "+1-555-678-6789"), + resource.TestCheckResourceAttr(resourceName, "alert_notifications", "false"), + resource.TestCheckResourceAttr(resourceName, "alerts_to_admins", "false"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMSecurityCenterContactExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).securityCenterContactsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + contactName := rs.Primary.Attributes["securityContacts"] + + resp, err := client.Get(ctx, contactName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Security Center Subscription Contact %q was not found: %+v", contactName, err) + } + + return fmt.Errorf("Bad: GetContact: %+v", err) + } + + return nil + } +} + +func testAccAzureRMSecurityCenterContact_template(email, phone string, notifications, adminAlerts bool) string { + return fmt.Sprintf(` +resource "azurerm_securitycenter_contact" "test" { + email = "%s" + phone = "%s" + + alert_notifications = %t + alerts_to_admins = %t +} +`, email, phone, notifications, adminAlerts) +}