diff --git a/examples/resource_lacework_alert_channel_newrelic/main.tf b/examples/resource_lacework_alert_channel_newrelic/main.tf new file mode 100644 index 000000000..380ce5cf0 --- /dev/null +++ b/examples/resource_lacework_alert_channel_newrelic/main.tf @@ -0,0 +1,7 @@ +provider "lacework" {} + +resource "lacework_alert_channel_newrelic" "example" { + name = "My New Relic Insights Channel Alert Example" + account_id = 2338053 + insert_key = "x-xx-xxxxxxxxxxxxxxxxxx" +} diff --git a/go.mod b/go.mod index 241f26f5b..943999365 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/hashicorp/terraform-plugin-test v1.4.3 // indirect github.com/hashicorp/terraform-svchost v0.0.0-20191119180714-d2e4933b9136 // indirect github.com/hashicorp/yamux v0.0.0-20200609203250-aecfd211c9ce // indirect - github.com/lacework/go-sdk v0.2.20-0.20210216214810-cac9fc27f696 + github.com/lacework/go-sdk v0.2.20-0.20210219030740-d7242b84a525 github.com/mattn/go-colorable v0.1.7 // indirect github.com/mitchellh/cli v1.1.1 // indirect github.com/mitchellh/go-homedir v1.1.0 diff --git a/go.sum b/go.sum index a996c98c4..ea20670c4 100644 --- a/go.sum +++ b/go.sum @@ -368,6 +368,8 @@ github.com/lacework/go-sdk v0.2.19-0.20210211001048-e41422624b52 h1:XgcQ1sTXFRqq github.com/lacework/go-sdk v0.2.19-0.20210211001048-e41422624b52/go.mod h1:yiEjWVHT4TjkZZ1pa9eS8fIfnnoVuzj1VNTIVEIsSKE= github.com/lacework/go-sdk v0.2.20-0.20210216214810-cac9fc27f696 h1:jd6J6ZjehkBH0MuO9Ttz7jyqTAcC2et5DxM4hV7LI28= github.com/lacework/go-sdk v0.2.20-0.20210216214810-cac9fc27f696/go.mod h1:yiEjWVHT4TjkZZ1pa9eS8fIfnnoVuzj1VNTIVEIsSKE= +github.com/lacework/go-sdk v0.2.20-0.20210219030740-d7242b84a525 h1:AWsfiGWpdtokfQDuGNhtP6oylCNDEVUaT5febFcJk30= +github.com/lacework/go-sdk v0.2.20-0.20210219030740-d7242b84a525/go.mod h1:yiEjWVHT4TjkZZ1pa9eS8fIfnnoVuzj1VNTIVEIsSKE= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= diff --git a/lacework/provider.go b/lacework/provider.go index 34f6e8aac..758dcafb8 100644 --- a/lacework/provider.go +++ b/lacework/provider.go @@ -55,6 +55,7 @@ func Provider() terraform.ResourceProvider { "lacework_alert_channel_gcp_pub_sub": resourceLaceworkAlertChannelGcpPubSub(), "lacework_alert_channel_jira_cloud": resourceLaceworkAlertChannelJiraCloud(), "lacework_alert_channel_jira_server": resourceLaceworkAlertChannelJiraServer(), + "lacework_alert_channel_newrelic": resourceLaceworkAlertChannelNewRelic(), "lacework_alert_channel_pagerduty": resourceLaceworkAlertChannelPagerDuty(), "lacework_alert_channel_microsoft_teams": resourceLaceworkAlertChannelMicrosoftTeams(), "lacework_alert_channel_slack": resourceLaceworkAlertChannelSlack(), diff --git a/lacework/resource_lacework_alert_channel_newrelic.go b/lacework/resource_lacework_alert_channel_newrelic.go new file mode 100644 index 000000000..dc1a70b9e --- /dev/null +++ b/lacework/resource_lacework_alert_channel_newrelic.go @@ -0,0 +1,215 @@ +package lacework + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/lacework/go-sdk/api" +) + +func resourceLaceworkAlertChannelNewRelic() *schema.Resource { + return &schema.Resource{ + Create: resourceLaceworkAlertChannelNewRelicCreate, + Read: resourceLaceworkAlertChannelNewRelicRead, + Update: resourceLaceworkAlertChannelNewRelicUpdate, + Delete: resourceLaceworkAlertChannelNewRelicDelete, + + Importer: &schema.ResourceImporter{ + State: importLaceworkIntegration, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "intg_guid": { + Type: schema.TypeString, + Computed: true, + }, + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "account_id": { + Type: schema.TypeInt, + Required: true, + }, + "insert_key": { + Type: schema.TypeString, + Required: true, + }, + "created_or_updated_time": { + Type: schema.TypeString, + Computed: true, + }, + "created_or_updated_by": { + Type: schema.TypeString, + Computed: true, + }, + "type_name": { + Type: schema.TypeString, + Computed: true, + }, + "org_level": { + Type: schema.TypeBool, + Computed: true, + }, + }, + } +} + +func resourceLaceworkAlertChannelNewRelicCreate(d *schema.ResourceData, meta interface{}) error { + var ( + lacework = meta.(*api.Client) + relic = api.NewNewRelicAlertChannel(d.Get("name").(string), + api.NewRelicChannelData{ + AccountID: d.Get("account_id").(int), + InsertKey: d.Get("insert_key").(string), + }, + ) + ) + if !d.Get("enabled").(bool) { + relic.Enabled = 0 + } + + log.Printf("[INFO] Creating %s integration with data:\n%+v\n", api.NewRelicChannelIntegration, relic) + response, err := lacework.Integrations.CreateNewRelicAlertChannel(relic) + if err != nil { + return err + } + + log.Println("[INFO] Verifying server response data") + err = validateNewRelicAlertChannelResponse(&response) + if err != nil { + return err + } + + integration := response.Data[0] + d.SetId(integration.IntgGuid) + d.Set("name", integration.Name) + d.Set("intg_guid", integration.IntgGuid) + d.Set("enabled", integration.Enabled == 1) + d.Set("created_or_updated_time", integration.CreatedOrUpdatedTime) + d.Set("created_or_updated_by", integration.CreatedOrUpdatedBy) + d.Set("type_name", integration.TypeName) + d.Set("org_level", integration.IsOrg == 1) + + log.Printf("[INFO] Created %s integration with guid: %v\n", api.NewRelicChannelIntegration, integration.IntgGuid) + return nil +} + +func resourceLaceworkAlertChannelNewRelicRead(d *schema.ResourceData, meta interface{}) error { + lacework := meta.(*api.Client) + + log.Printf("[INFO] Reading %s integration with guid: %v\n", api.NewRelicChannelIntegration, d.Id()) + response, err := lacework.Integrations.GetNewRelicAlertChannel(d.Id()) + if err != nil { + return err + } + + for _, integration := range response.Data { + if integration.IntgGuid == d.Id() { + d.Set("name", integration.Name) + d.Set("intg_guid", integration.IntgGuid) + d.Set("enabled", integration.Enabled == 1) + d.Set("created_or_updated_time", integration.CreatedOrUpdatedTime) + d.Set("created_or_updated_by", integration.CreatedOrUpdatedBy) + d.Set("type_name", integration.TypeName) + d.Set("org_level", integration.IsOrg == 1) + d.Set("account_id", integration.Data.AccountID) + d.Set("insert_key", integration.Data.InsertKey) + + log.Printf("[INFO] Read %s integration with guid: %v\n", + api.NewRelicChannelIntegration, integration.IntgGuid) + return nil + } + } + + d.SetId("") + return nil +} + +func resourceLaceworkAlertChannelNewRelicUpdate(d *schema.ResourceData, meta interface{}) error { + var ( + lacework = meta.(*api.Client) + relic = api.NewNewRelicAlertChannel(d.Get("name").(string), + api.NewRelicChannelData{ + AccountID: d.Get("account_id").(int), + InsertKey: d.Get("insert_key").(string), + }, + ) + ) + + if !d.Get("enabled").(bool) { + relic.Enabled = 0 + } + + relic.IntgGuid = d.Id() + + log.Printf("[INFO] Updating %s integration with data:\n%+v\n", api.NewRelicChannelIntegration, relic) + response, err := lacework.Integrations.UpdateNewRelicAlertChannel(relic) + if err != nil { + return err + } + + log.Println("[INFO] Verifying server response data") + err = validateNewRelicAlertChannelResponse(&response) + if err != nil { + return err + } + + integration := response.Data[0] + d.Set("name", integration.Name) + d.Set("intg_guid", integration.IntgGuid) + d.Set("enabled", integration.Enabled == 1) + d.Set("created_or_updated_time", integration.CreatedOrUpdatedTime) + d.Set("created_or_updated_by", integration.CreatedOrUpdatedBy) + d.Set("type_name", integration.TypeName) + d.Set("org_level", integration.IsOrg == 1) + + log.Printf("[INFO] Updated %s integration with guid: %v\n", api.NewRelicChannelIntegration, d.Id()) + return nil +} + +func resourceLaceworkAlertChannelNewRelicDelete(d *schema.ResourceData, meta interface{}) error { + lacework := meta.(*api.Client) + + log.Printf("[INFO] Deleting %s integration with guid: %v\n", api.NewRelicChannelIntegration, d.Id()) + _, err := lacework.Integrations.Delete(d.Id()) + if err != nil { + return err + } + + log.Printf("[INFO] Deleted %s integration with guid: %v\n", api.NewRelicChannelIntegration, d.Id()) + return nil +} + +func validateNewRelicAlertChannelResponse(response *api.NewRelicAlertChannelResponse) error { + if len(response.Data) == 0 { + msg := ` +Unable to read sever response data. (empty 'data' field) + +This was an unexpected behavior, verify that your integration has been +created successfully and report this issue to support@lacework.net +` + return fmt.Errorf(msg) + } + + if len(response.Data) > 1 { + msg := ` +There is more that one integration inside the server response data. + +List of integrations: +` + for _, integration := range response.Data { + msg = msg + fmt.Sprintf("\t%s: %s\n", integration.IntgGuid, integration.Name) + } + msg = msg + unexpectedBehaviorMsg() + return fmt.Errorf(msg) + } + + return nil +} diff --git a/lacework/resource_lacework_alert_channel_newrelic_test.go b/lacework/resource_lacework_alert_channel_newrelic_test.go new file mode 100644 index 000000000..acfacd6cc --- /dev/null +++ b/lacework/resource_lacework_alert_channel_newrelic_test.go @@ -0,0 +1,141 @@ +package lacework + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/lacework/go-sdk/api" +) + +const ( + testAccAlertChannelNewRelicResourceType = "lacework_alert_channel_newrelic" + testAccAlertChannelNewRelicResourceName = "example" + + testAccAlertChannelNewRelicAccountID = "ACCOUNT_ID" + testAccAlertChannelNewRelicInsertKey = "INSERT_KEY" +) + +func TestAccAlertChannelNewRelic(t *testing.T) { + resourceTypeAndName := fmt.Sprintf("%s.%s", + testAccAlertChannelNewRelicResourceType, + testAccAlertChannelNewRelicResourceName, + ) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccAlertChannelNewRelicEnvVarsPreCheck(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAlertChannelNewRelicDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAlertChannelNewRelicConfig( + true, + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckAlertChannelNewRelicExists(resourceTypeAndName), + resource.TestCheckResourceAttr(resourceTypeAndName, "enabled", "true"), + ), + }, + { + Config: testAccAlertChannelNewRelicConfig( + false, + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckAlertChannelNewRelicExists(resourceTypeAndName), + resource.TestCheckResourceAttr(resourceTypeAndName, "enabled", "false"), + ), + }, + }, + }) +} + +func testAccCheckAlertChannelNewRelicDestroy(s *terraform.State) error { + lacework := testAccProvider.Meta().(*api.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != testAccAlertChannelNewRelicResourceType { + continue + } + + response, err := lacework.Integrations.GetNewRelicAlertChannel(rs.Primary.ID) + if err != nil { + return err + } + + for _, integration := range response.Data { + if integration.IntgGuid == rs.Primary.ID { + return fmt.Errorf( + "the %s integration (%s) still exists", + api.NewRelicChannelIntegration, rs.Primary.ID, + ) + } + } + } + + return nil +} + +func testAccCheckAlertChannelNewRelicExists(resourceTypeAndName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + lacework := testAccProvider.Meta().(*api.Client) + + rs, ok := s.RootModule().Resources[resourceTypeAndName] + if !ok { + return fmt.Errorf("resource (%s) not found", resourceTypeAndName) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("resource (%s) ID not set", resourceTypeAndName) + } + + response, err := lacework.Integrations.GetNewRelicAlertChannel(rs.Primary.ID) + if err != nil { + return err + } + + if len(response.Data) < 1 { + return fmt.Errorf("the %s integration (%s) doesn't exist", + api.NewRelicChannelIntegration, rs.Primary.ID) + } + + for _, integration := range response.Data { + if integration.IntgGuid == rs.Primary.ID { + return nil + } + } + + return fmt.Errorf("the %s integration (%s) doesn't exist", + api.NewRelicChannelIntegration, rs.Primary.ID) + } +} + +func testAccAlertChannelNewRelicEnvVarsPreCheck(t *testing.T) { + if v := os.Getenv(testAccAlertChannelNewRelicAccountID); v == "" { + t.Fatalf("%s must be set for acceptance tests", testAccAlertChannelNewRelicAccountID) + } + if v := os.Getenv(testAccAlertChannelNewRelicInsertKey); v == "" { + t.Fatalf("%s must be set for acceptance tests", testAccAlertChannelNewRelicInsertKey) + } +} + +func testAccAlertChannelNewRelicConfig(enabled bool) string { + return fmt.Sprintf(` + resource "%s" "%s" { + name = "integration test" + enabled = %t + account_id = "%s" + insert_key = "%s" + } + `, + testAccAlertChannelNewRelicResourceType, + testAccAlertChannelNewRelicResourceName, + enabled, + os.Getenv(testAccAlertChannelNewRelicAccountID), + os.Getenv(testAccAlertChannelNewRelicInsertKey), + ) +} diff --git a/vendor/github.com/lacework/go-sdk/api/integration_alert_channels_new_relic.go b/vendor/github.com/lacework/go-sdk/api/integration_alert_channels_new_relic.go new file mode 100644 index 000000000..7b1cb3b86 --- /dev/null +++ b/vendor/github.com/lacework/go-sdk/api/integration_alert_channels_new_relic.go @@ -0,0 +1,100 @@ +// +// Author:: Darren Murray () +// Copyright:: Copyright 2020, Lacework Inc. +// License:: Apache License, Version 2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package api + +// NewNewRelicAlertChannel returns an instance of NewRelicAlertChannel +// with the provided name and data. +// +// Basic usage: Initialize a new NewRelicAlertChannel struct, then +// use the new instance to do CRUD operations +// +// client, err := api.NewClient("account") +// if err != nil { +// return err +// } +// +// newRelicChannel := api.NewNewRelicAlertChannel("foo", +// api.NewRelicChannelData{ +// AccountID: 2338053, +// InsertKey: "x-xx-xxxxxxxxxxxxxxxxxx", +// }, +// ) +// +// client.Integrations.CreateNewRelicAlertChannel(newRelicChannel) +// +func NewNewRelicAlertChannel(name string, data NewRelicChannelData) NewRelicAlertChannel { + return NewRelicAlertChannel{ + commonIntegrationData: commonIntegrationData{ + Name: name, + Type: NewRelicChannelIntegration.String(), + Enabled: 1, + }, + Data: data, + } +} + +// CreateNewRelicAlertChannel creates an NEW_RELIC_INSIGHTS alert channel integration on the Lacework Server +func (svc *IntegrationsService) CreateNewRelicAlertChannel(integration NewRelicAlertChannel) ( + response NewRelicAlertChannelResponse, + err error, +) { + err = svc.create(integration, &response) + return +} + +// GetNewRelicAlertChannel gets an NEW_RELIC_INSIGHTS alert channel integration that matches with +// the provided integration guid on the Lacework Server +func (svc *IntegrationsService) GetNewRelicAlertChannel(guid string) ( + response NewRelicAlertChannelResponse, + err error, +) { + err = svc.get(guid, &response) + return +} + +// UpdateNewRelicAlertChannel updates a single NEW_RELIC_INSIGHTS alert channel integration +func (svc *IntegrationsService) UpdateNewRelicAlertChannel(data NewRelicAlertChannel) ( + response NewRelicAlertChannelResponse, + err error, +) { + err = svc.update(data.IntgGuid, data, &response) + return +} + +// ListNewRelicAlertChannel lists the NEW_RELIC_INSIGHTS external integrations available on the Lacework Server +func (svc *IntegrationsService) ListNewRelicAlertChannel() (response NewRelicAlertChannelResponse, err error) { + err = svc.listByType(NewRelicChannelIntegration, &response) + return +} + +type NewRelicAlertChannelResponse struct { + Data []NewRelicAlertChannel `json:"data"` + Ok bool `json:"ok"` + Message string `json:"message"` +} + +type NewRelicAlertChannel struct { + commonIntegrationData + Data NewRelicChannelData `json:"DATA"` +} + +type NewRelicChannelData struct { + AccountID int `json:"ACCOUNT_ID" mapstructure:"ACCOUNT_ID"` + InsertKey string `json:"INSERT_KEY" mapstructure:"INSERT_KEY"` +} diff --git a/vendor/github.com/lacework/go-sdk/api/integrations.go b/vendor/github.com/lacework/go-sdk/api/integrations.go index 427a1cefb..9c5491d20 100644 --- a/vendor/github.com/lacework/go-sdk/api/integrations.go +++ b/vendor/github.com/lacework/go-sdk/api/integrations.go @@ -55,6 +55,9 @@ const ( // GCP Pub Sub alert channel integration type GcpPubSubChannelIntegration + // New Relic Insights alert channel integration type + NewRelicChannelIntegration + // Azure Config integration type AzureCfgIntegration @@ -106,6 +109,7 @@ var IntegrationTypes = map[integrationType]string{ GcpCfgIntegration: "GCP_CFG", GcpAuditLogIntegration: "GCP_AT_SES", GcpPubSubChannelIntegration: "GCP_PUBSUB", + NewRelicChannelIntegration: "NEW_RELIC_INSIGHTS", AzureCfgIntegration: "AZURE_CFG", AzureActivityLogIntegration: "AZURE_AL_SEQ", ContainerRegistryIntegration: "CONT_VULN_CFG", diff --git a/vendor/modules.txt b/vendor/modules.txt index 515e0aaf8..ebacd34c1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -286,7 +286,7 @@ github.com/jstemmer/go-junit-report/formatter github.com/jstemmer/go-junit-report/parser # github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd github.com/kevinburke/ssh_config -# github.com/lacework/go-sdk v0.2.20-0.20210216214810-cac9fc27f696 +# github.com/lacework/go-sdk v0.2.20-0.20210219030740-d7242b84a525 ## explicit github.com/lacework/go-sdk/api github.com/lacework/go-sdk/internal/array diff --git a/website/docs/r/alert_channel_newrelic.html.markdown b/website/docs/r/alert_channel_newrelic.html.markdown new file mode 100644 index 000000000..d07749af1 --- /dev/null +++ b/website/docs/r/alert_channel_newrelic.html.markdown @@ -0,0 +1,43 @@ +--- +subcategory: "Alert Channels" +layout: "lacework" +page_title: "Lacework: lacework_alert_channel_newrelic" +description: |- + Create and manage New Relic Insights Alert Channel integrations +--- + +# lacework\_alert\_channel\_newrelic + +You can configure a Lacework alert channel to forward alerts to New Relic using the Insights API. + +To find more information about the New Relic Insights alert channel integration, see the [Lacework support documentation](https://support.lacework.com/hc/en-us/articles/360005842354-New-Relic). + +## Example Usage + +```hcl +resource "lacework_alert_channel_newrelic" "example" { + name = "Example New Relic Insights Alert" + account_id = 2338053 + insert_key = "x-xx-xxxxxxxxxxxxxxxxxx" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The Alert Channel integration name. +* `account_id` - (Required) The New Relic account ID. +* `insert_key` - (Required) The New Relic Insert API key. +* `enabled` - (Optional) The state of the external integration. Defaults to `true`. + +## Import + +A Lacework New Relic Insights Alert Channel integration can be imported using a `INT_GUID`, e.g. + +``` +$ terraform import lacework_alert_channel_newrelic.example EXAMPLE_1234BAE1E42182964D23973F44CFEA3C4AB63B99E9A1EC5 +``` +-> **Note:** To retreive the `INT_GUID` from existing integrations in your account, use the + Lacework CLI command `lacework integration list`. To install this tool follow + [this documentation](https://github.com/lacework/go-sdk/wiki/CLI-Documentation#installation). diff --git a/website/lacework.erb b/website/lacework.erb index ceebe0f68..0754aba5e 100644 --- a/website/lacework.erb +++ b/website/lacework.erb @@ -64,6 +64,9 @@
  • lacework_alert_channel_jira_server
  • +
  • + lacework_alert_channel_newrelic +
  • lacework_alert_channel_pagerduty