diff --git a/bin/bootstrap.sh b/bin/bootstrap.sh index 1adaabba..3cdcc100 100755 --- a/bin/bootstrap.sh +++ b/bin/bootstrap.sh @@ -71,7 +71,7 @@ sed_i() { export -f sed_i # Define the file types to search for -file_types=("yml" "yaml" "erb" "rb" "json") +file_types=("yml" "yaml" "erb" "rb" "json" "tf") for file_type in "${file_types[@]}" do diff --git a/config/newrelic.yml b/config/newrelic.yml index f8f8f6c4..ccc3daa3 100644 --- a/config/newrelic.yml +++ b/config/newrelic.yml @@ -11,7 +11,7 @@ common: &default_settings # Required license key associated with your New Relic account. - license_key: <%= ENV['NEW_RELIC_API_KEY'] %> + license_key: <%= ENV['NEW_RELIC_LICENSE_KEY'] %> # Your application name. Renaming here affects where data displays in New # Relic. For more details, see https://docs.newrelic.com/docs/apm/new-relic-apm/maintenance/renaming-applications diff --git a/infra/new_relic/.env.sample b/infra/new_relic/.env.sample new file mode 100644 index 00000000..a7ed966b --- /dev/null +++ b/infra/new_relic/.env.sample @@ -0,0 +1,5 @@ +NEW_RELIC_ACCOUNT_ID=1234 +NEW_RELIC_API_KEY=NRAK-** +NEW_RELIC_REGION=US + +TF_VAR_account_id=1234 diff --git a/infra/new_relic/.gitignore b/infra/new_relic/.gitignore new file mode 100644 index 00000000..b10e9b99 --- /dev/null +++ b/infra/new_relic/.gitignore @@ -0,0 +1,3 @@ +.terraform +.env +terraform.tfstate* diff --git a/infra/new_relic/.terraform.lock.hcl b/infra/new_relic/.terraform.lock.hcl new file mode 100644 index 00000000..750b9902 --- /dev/null +++ b/infra/new_relic/.terraform.lock.hcl @@ -0,0 +1,27 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/newrelic/newrelic" { + version = "3.61.0" + hashes = [ + "h1:hvVjaulLrMGWjyJbFmlWoLFNO//espYTfUamuOWU5VY=", + "zh:0988ea702449dfe50f1a1d5b648a6d21e705ed96a79494526398e63a14b5cb49", + "zh:0fbf18f31ba85e3b6e440f19eb64e177811ffc833c68f99bf698a3285fe78d9f", + "zh:1b1d7e5dd63754604c6d4aa1494968bb9f74f594ad2a4e6391a170922ef5870f", + "zh:3daa7f87674f1159a020c1b15014990cab1a0331c0acc9b5cb4176c151ea5977", + "zh:55e18268e3f0233ac9905c65d8404708336edbead6d795c5e6481fd77d839788", + "zh:567a0204309a6de9d27ce2426ce498516604cfe947b923724a72b6f8b0c5c1c2", + "zh:5b56fdb5837e01e8701bf7888dd59a222b75a9926e05801054e0f5324a972276", + "zh:5d51077e392080cad2f981999c26ad62f242ed37b3fbedc0e57501b55c3e8b99", + "zh:7612219e2f9d62179b2b579d091f1c43c4aecfd0572d2be89cf9c762daf2d095", + "zh:7a7dcf55bfcc379ae0667a4181a90fdcbaef3cbf80b1f60d8770ecbdc99a5eee", + "zh:7e3565d8d4ed59f414d46c7beb05bdcdad307736fc77c9030af70e9c4658dbb0", + "zh:87f01c33ac82c6fea84aaae1c6aadb7f69931b3b10e7642f70442163e0bd0e3a", + "zh:8d8cfa78278ad5dcae90d5a16450e6cf73138028852647a33ecb9a2915aff483", + "zh:9cb4027e1099c70428e3b0cdd8c64e0316cf6967ef7e436c4fd45ed2cee46407", + "zh:ac0ba9be2fba2c61aac29b0ee8b766ce5f7ecbabd06ebcf558eb718d7a0f996e", + "zh:ced2c73d3d63a540bf7e4634996e9fa22e9c99dd13b87fe369d0da169c46fb3f", + "zh:df27dc9d839ee0279afca8c2594a72e301c639e7d18a0eaad81dda177bcf9289", + "zh:fbd1fee2c9df3aa19cf8851ce134dea6e45ea01cb85695c1726670c285797e25", + ] +} diff --git a/infra/new_relic/README.md b/infra/new_relic/README.md new file mode 100644 index 00000000..87587eb9 --- /dev/null +++ b/infra/new_relic/README.md @@ -0,0 +1,44 @@ +# Preconditions + +## Entities +Create one New Relic entity for each environment. Current setup assumes one entity for "development", "staging" and "production" environments. For example, if your app name is "rails_api_base", it expects to have "rails_api_base - development" entity for dev. + +## Slack +Set up the Slack integration from [New Relic](https://docs.newrelic.com/docs/alerts/get-notified/notification-integrations/). You will need to replace `slack_destination_id` local variable with the id generated by the integration, and `slack_channel_id` with the channel id where you want to send notifications. + +# Installation + +Follow the [Terraform installation docs](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli) to install Terraform CLI. + +## Environment variables +Make sure you copy the `.env.sample` file into `.env` and fill all the variables. + +## Usage + +There are only three commands that need to be run in order to apply this terraform template and fully setup your New Relic monitoring. + +```bash +# Init +terraform init + +# Plan +terraform plan + +# Apply +terraform apply +``` + +# Docker usage + +If you want to use Docker to avoid installing Terraform, we recommend to use one of the latest versions of the Dockerfile. + +```bash +# Init +docker run -w /app -v .:/app --env-file .env -it hashicorp/terraform:latest init + +# Plan +docker run -w /app -v .:/app --env-file .env -it hashicorp/terraform:latest plan + +# Apply +docker run -w /app -v .:/app --env-file .env -it hashicorp/terraform:latest apply +``` diff --git a/infra/new_relic/dashboard.json.tftpl b/infra/new_relic/dashboard.json.tftpl new file mode 100644 index 00000000..3ff8d761 --- /dev/null +++ b/infra/new_relic/dashboard.json.tftpl @@ -0,0 +1,524 @@ +{ + "name": "Activerecord - ${app_name}", + "description": "Basic dashboard for Activerecord", + "permissions": "PUBLIC_READ_WRITE", + "pages": [ + { + "name": "Overview", + "description": "Overview", + "widgets": [ + { + "title": "", + "layout": { + "column": 1, + "row": 1, + "width": 5, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.markdown" + }, + "rawConfiguration": { + "text": "\n![Transactions](https://raw.githubusercontent.com/newrelic/newrelic-quickstarts/main/quickstarts/ruby/curb/images/Transactions.png)\n" + } + }, + { + "title": "Transactions Overview", + "layout": { + "column": 6, + "row": 1, + "width": 7, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.billboard" + }, + "rawConfiguration": { + "dataFormatters": [], + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "FROM Transaction SELECT count(*) as 'Total transactions', average(duration) as 'Average duration (s)', percentile(duration, 90) as 'Slowest 10%/duration' SINCE 1 week ago WHERE appName = '${app_name}'" + } + ], + "thresholds": [] + } + }, + { + "title": "", + "layout": { + "column": 1, + "row": 4, + "width": 5, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.markdown" + }, + "rawConfiguration": { + "text": "\n![Errors](https://raw.githubusercontent.com/newrelic/newrelic-quickstarts/main/quickstarts/ruby/curb/images/Errors.png)\n" + } + }, + { + "title": "Errors Overview", + "layout": { + "column": 6, + "row": 4, + "width": 7, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.billboard" + }, + "rawConfiguration": { + "dataFormatters": [], + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "FROM Transaction SELECT count(*) as 'Total transactions',percentage(count(*), WHERE error IS true) as 'Failed transactions In %', count(*) * percentage(count(*), WHERE error IS true) / 100 as 'Failed transactions' SINCE 1 week ago WHERE appName = '${app_name}'" + } + ], + "thresholds": [] + } + }, + { + "title": null, + "layout": { + "column": 1, + "row": 7, + "width": 5, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.markdown" + }, + "rawConfiguration": { + "text": "\n![VM Metrics](https://raw.githubusercontent.com/newrelic/newrelic-quickstarts/main/quickstarts/ruby/curb/images/VM-metrics.png)\n" + } + }, + { + "title": "VM Overview", + "layout": { + "column": 6, + "row": 7, + "width": 7, + "height": 3 + }, + "linkedEntityGuids": null, + "visualization": { + "id": "viz.billboard" + }, + "rawConfiguration": { + "dataFormatters": [], + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "FROM Metric SELECT average(apm.service.cpu.usertime.utilization) * 100 as 'Average CPU utilization ', average(apm.service.memory.physical) as 'Average physical memory' WHERE appName = '${app_name}' SINCE 1 WEEK AGO" + } + ], + "thresholds": [] + } + } + ] + }, + { + "name": "Errors", + "description": "Errors", + "widgets": [ + { + "visualization": { + "id": "viz.billboard" + }, + "layout": { + "column": 1, + "row": 1, + "height": 3, + "width": 8 + }, + "title": "Error Overview", + "rawConfiguration": { + "dataFormatters": [], + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "FROM Transaction SELECT count(*) as 'Total transactions', percentage(count(*), WHERE error IS true) as 'Failed transactions in %', count(*) * percentage(count(*), WHERE error IS true) / 100 as 'Failed transactions' SINCE 1 week ago WHERE appName = '${app_name}'" + } + ], + "thresholds": [] + }, + "linkedEntityGuids": null + }, + { + "visualization": { + "id": "viz.table" + }, + "layout": { + "column": 9, + "row": 1, + "height": 3, + "width": 4 + }, + "title": "Top 10 Failed Web Transactions", + "rawConfiguration": { + "dataFormatters": [], + "facet": { + "showOtherSeries": false + }, + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "Select percentage(count(*), WHERE error IS true) from Transaction WHERE transactionType = 'Web' facet name SINCE last week WHERE appName = '${app_name}'" + } + ] + }, + "linkedEntityGuids": null + }, + { + "visualization": { + "id": "viz.line" + }, + "layout": { + "column": 1, + "row": 4, + "height": 3, + "width": 4 + }, + "title": "Transactions Errors Today Compared With 1 Week Ago", + "rawConfiguration": { + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "SELECT count(*) from Transaction where response.status = '404' and transactionType = 'Web' TIMESERIES 10 minutes since today COMPARE WITH 1 week ago WHERE appName = '${app_name}'" + } + ], + "yAxisLeft": { + "zero": true + } + }, + "linkedEntityGuids": null + }, + { + "visualization": { + "id": "viz.billboard" + }, + "layout": { + "column": 5, + "row": 4, + "height": 3, + "width": 4 + }, + "title": "Latest Error", + "rawConfiguration": { + "dataFormatters": [], + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "FROM TransactionError SELECT latest(timestamp) as 'Latest error' SINCE last week WHERE appName = '${app_name}'" + } + ], + "thresholds": [] + }, + "linkedEntityGuids": null + }, + { + "visualization": { + "id": "viz.table" + }, + "layout": { + "column": 9, + "row": 4, + "height": 3, + "width": 4 + }, + "title": "Top 10 Failed Non-Web Transactions", + "rawConfiguration": { + "dataFormatters": [], + "facet": { + "showOtherSeries": false + }, + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "Select percentage(count(*), WHERE error IS true) from Transaction WHERE transactionType != 'Web' facet name SINCE last week WHERE appName = '${app_name}'" + } + ] + }, + "linkedEntityGuids": null + } + ] + }, + { + "name": "VM Metrics", + "description": "VM Metrics", + "widgets": [ + { + "visualization": { + "id": "viz.billboard" + }, + "layout": { + "column": 1, + "row": 1, + "height": 3, + "width": 7 + }, + "title": "VM Overview", + "rawConfiguration": { + "dataFormatters": [], + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "FROM Metric SELECT average(apm.service.cpu.usertime.utilization) * 100 as 'Average CPU utilization ', average(apm.service.memory.physical) as 'Average physical memory' WHERE appName = '${app_name}' SINCE 1 WEEK AGO" + } + ], + "thresholds": [] + }, + "linkedEntityGuids": null + }, + { + "visualization": { + "id": "viz.area" + }, + "layout": { + "column": 8, + "row": 1, + "height": 3, + "width": 5 + }, + "title": "CPU Utilization", + "rawConfiguration": { + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "SELECT rate(sum(apm.service.cpu.usertime.utilization), 1 second) * 100 as cpuUsage FROM Metric WHERE appName = '${app_name}' SINCE 30 minutes ago TIMESERIES" + } + ] + }, + "linkedEntityGuids": null + }, + { + "visualization": { + "id": "viz.area" + }, + "layout": { + "column": 1, + "row": 4, + "height": 3, + "width": 12 + }, + "title": "Average Physical Memory", + "rawConfiguration": { + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "SELECT average(apm.service.memory.physical) FROM Metric WHERE appName = '${app_name}' SINCE 30 minutes ago TIMESERIES" + } + ] + }, + "linkedEntityGuids": null + } + ] + }, + { + "name": "Transactions", + "description": "Transactions", + "widgets": [ + { + "visualization": { + "id": "viz.billboard" + }, + "layout": { + "column": 1, + "row": 1, + "height": 3, + "width": 7 + }, + "title": "Transactions Overview", + "rawConfiguration": { + "dataFormatters": [], + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "FROM Transaction SELECT count(*), average(duration) as 'Average duration (s)', percentile(duration, 90) as 'Slowest 10%/duration' WHERE appName = '${app_name}' SINCE 1 WEEK AGO" + } + ], + "thresholds": [] + }, + "linkedEntityGuids": null + }, + { + "visualization": { + "id": "viz.pie" + }, + "layout": { + "column": 8, + "row": 1, + "height": 3, + "width": 5 + }, + "title": "Most Popular Transactions", + "rawConfiguration": { + "facet": { + "showOtherSeries": false + }, + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "SELECT count(*) FROM Transaction WHERE (transactionType = 'Web') SINCE last week EXTRAPOLATE FACET name WHERE appName = '${app_name}'" + } + ] + }, + "linkedEntityGuids": null + }, + { + "visualization": { + "id": "viz.line" + }, + "layout": { + "column": 1, + "row": 4, + "height": 3, + "width": 4 + }, + "title": "Adpex Score Today Compared With 1 Week Ago", + "rawConfiguration": { + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "SELECT apdex(duration,t: 0.4) FROM Transaction TIMESERIES SINCE today COMPARE WITH 1 week ago WHERE appName = '${app_name}'" + } + ], + "yAxisLeft": { + "zero": true + } + }, + "linkedEntityGuids": null + }, + { + "visualization": { + "id": "viz.line" + }, + "layout": { + "column": 5, + "row": 4, + "height": 3, + "width": 4 + }, + "title": "Throughput Today Compared With 1 Week Ago", + "rawConfiguration": { + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "SELECT count(*) from Transaction TIMESERIES 1 hour since today COMPARE WITH 1 week ago WHERE appName = '${app_name}'" + } + ], + "yAxisLeft": { + "zero": true + } + }, + "linkedEntityGuids": null + }, + { + "visualization": { + "id": "viz.line" + }, + "layout": { + "column": 9, + "row": 4, + "height": 3, + "width": 4 + }, + "title": "Average Transaction Duration Today Compared With 1 Week Ago", + "rawConfiguration": { + "dataFormatters": [], + "legend": { + "enabled": true + }, + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "SELECT average(duration) FROM Transaction TIMESERIES SINCE today COMPARE WITH 1 week ago WHERE appName = '${app_name}'" + } + ], + "yAxisLeft": { + "zero": true + } + }, + "linkedEntityGuids": null + }, + { + "visualization": { + "id": "viz.bar" + }, + "layout": { + "column": 1, + "row": 7, + "height": 4, + "width": 6 + }, + "title": "Top 5 Slowest Transactions", + "rawConfiguration": { + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "SELECT max(duration) FROM Transaction WHERE (transactionType = 'Web') SINCE 1 week ago LIMIT 5 EXTRAPOLATE FACET name WHERE appName = '${app_name}'" + } + ], + "other": { + "visible": false + }, + "yAxis": { + "label": "Seconds" + } + }, + "linkedEntityGuids": null + }, + { + "visualization": { + "id": "viz.table" + }, + "layout": { + "column": 7, + "row": 7, + "height": 4, + "width": 6 + }, + "title": "Transaction Duration Overview", + "rawConfiguration": { + "dataFormatters": [], + "facet": { + "showOtherSeries": false + }, + "nrqlQueries": [ + { + "accountId": "${account_id}", + "query": "SELECT average(duration), max(duration), min(duration) FROM Transaction WHERE appName = '${app_name}' FACET name" + } + ] + }, + "linkedEntityGuids": null + } + ] + } + ], + "variables": [] +} diff --git a/infra/new_relic/main.tf b/infra/new_relic/main.tf new file mode 100644 index 00000000..fbb9c60c --- /dev/null +++ b/infra/new_relic/main.tf @@ -0,0 +1,268 @@ +terraform { + required_version = "~> 1.0" + + required_providers { + newrelic = { + source = "newrelic/newrelic" + version = "~> 3.0" + } + } +} + +locals { + transactions_per_environment = { + "Admin Users Index" = { + transaction_name = "Controller/admin/admin_users/index" + p90_latency_threshold = 0.2 + } + } + # Must be an exact match to your application name in New Relic + app_name = "rails_api_base" + app_environments = ["development", "staging", "production"] + slack_channel_id = "your-slack-channel-id" + slack_destination_id = "your-slack-destination-id" + devs_emails = ["devs@example.com"] + app_names = [ + for env in local.app_environments : "${local.app_name} - ${env}" + ] + transactions = flatten([ + for app in local.app_names : [ + for t_k, t_v in local.transactions_per_environment : merge({ + app_name = app, + friendly_name = t_k + }, t_v) + ] + ]) +} + +# We use env variables to configure the provider +provider "newrelic" {} + +variable "account_id" { + type = number + description = "New Relic account ID." +} + +data "newrelic_entity" "app" { + for_each = toset(local.app_names) + name = each.value + domain = "APM" + type = "APPLICATION" +} + +resource "newrelic_alert_policy" "rails_app_policy" { + for_each = toset(local.app_names) + name = "Rails app policy - ${each.value}" +} + +# Throughput condition +resource "newrelic_nrql_alert_condition" "low_throughput" { + for_each = toset(local.app_names) + policy_id = newrelic_alert_policy.rails_app_policy[each.value].id + type = "static" + name = "Low Throughput - ${each.value}" + description = "Low Throughput" + enabled = true + violation_time_limit_seconds = 259200 # 3 days + + nrql { + query = "FROM Transaction SELECT count(*) WHERE appName = '${each.value}'" + } + + critical { + threshold = 5 + threshold_duration = 300 + threshold_occurrences = "ALL" + operator = "below" + } +} + +# Error rate condition +resource "newrelic_nrql_alert_condition" "error_alert" { + for_each = toset(local.app_names) + policy_id = newrelic_alert_policy.rails_app_policy[each.value].id + type = "static" + name = "Error alert - ${each.value}" + description = "Error Alert" + enabled = true + violation_time_limit_seconds = 259200 # 3 days + + nrql { + query = "SELECT count(*) FROM Transaction WHERE error IS true AND appName = '${each.value}'" + } + + critical { + threshold = 1 + threshold_duration = 60 + threshold_occurrences = "ALL" + operator = "above_or_equals" + } +} + +####################################### +# P90 and P95 Alerts # +####################################### + +# Global P90 Alert +resource "newrelic_nrql_alert_condition" "p90_latency_alert" { + for_each = toset(local.app_names) + policy_id = newrelic_alert_policy.rails_app_policy[each.value].id + type = "static" + name = "P90 Response Time - ${each.value}" + description = "High P90 Response Time" + enabled = true + violation_time_limit_seconds = 259200 # 3 days + + nrql { + query = "SELECT percentile(duration, 90) FROM Transaction WHERE appName = '${each.value}'" + } + + critical { + operator = "above" + threshold = 1 + threshold_duration = 300 + threshold_occurrences = "ALL" + } +} + +# Global P95 Alert +resource "newrelic_nrql_alert_condition" "p95_alert" { + for_each = toset(local.app_names) + policy_id = newrelic_alert_policy.rails_app_policy[each.value].id + type = "static" + name = "P95 Response Time - ${each.value}" + description = "High P95 Response Time" + enabled = true + violation_time_limit_seconds = 259200 # 3 days + + nrql { + query = "SELECT percentile(duration, 95) FROM Transaction WHERE appName = '${each.value}'" + } + + critical { + threshold = 1 + threshold_duration = 300 + threshold_occurrences = "ALL" + operator = "above" + } +} + +# Generate a P90 alert for each transaction defined in locals.transactions +resource "newrelic_nrql_alert_condition" "transaction_p90_alert" { + for_each = { for t in local.transactions : "${t.app_name}|${t.transaction_name}" => t } + policy_id = newrelic_alert_policy.rails_app_policy[each.value.app_name].id + type = "static" + name = "P90 Alert on ${each.value.friendly_name} - ${each.value.app_name}" + description = "P90 Alert on ${each.value.transaction_name} in ${each.value.app_name}" + enabled = true + violation_time_limit_seconds = 259200 # 3 days + + nrql { + query = "SELECT percentile(duration, 90) FROM Transaction WHERE appName = '${each.value.app_name}' AND name = '${each.value.transaction_name}'" + } + + critical { + threshold = each.value.p90_latency_threshold + threshold_duration = 300 + threshold_occurrences = "ALL" + operator = "above" + } +} + +####################################### +# DASHBOARDS # +####################################### + +resource "newrelic_one_dashboard_json" "activerecord_dashboard" { + for_each = toset(local.app_names) + json = templatefile("${path.module}/dashboard.json.tftpl", { + app_name = each.value, + account_id = var.account_id + }) +} + +####################################### +# NOTIFICATIONS # +####################################### + +resource "newrelic_notification_destination" "email_destination" { + for_each = toset(local.app_names) + name = "Email destination - ${each.value}" + type = "EMAIL" + + property { + key = "email" + value = join(",", local.devs_emails) + } +} + +resource "newrelic_notification_channel" "email_channel" { + for_each = toset(local.app_names) + name = "Email channel - ${each.value}" + type = "EMAIL" + destination_id = newrelic_notification_destination.email_destination[each.value].id + product = "IINT" + + property { + key = "subject" + value = "New Alert" + } +} + +resource "newrelic_workflow" "workflow" { + for_each = toset(local.app_names) + name = "Workflow - ${each.value}" + enabled = true + muting_rules_handling = "NOTIFY_ALL_ISSUES" + + issues_filter { + name = "Filter - ${each.value}" + type = "FILTER" + + predicate { + attribute = "accumulations.policyName" + operator = "EXACTLY_MATCHES" + values = [ + for app in local.app_names : newrelic_alert_policy.rails_app_policy[app].name + ] + } + } + + destination { + channel_id = newrelic_notification_channel.email_channel[each.value].id + } +} +resource "newrelic_notification_channel" "slack_channel" { + name = "Slack channel" + type = "SLACK" + destination_id = local.slack_destination_id + product = "IINT" + + property { + key = "channelId" + value = local.slack_channel_id + } +} + +resource "newrelic_workflow" "slack_workflow" { + name = "Slack workflow" + enabled = true + muting_rules_handling = "NOTIFY_ALL_ISSUES" + + issues_filter { + name = "Filter" + type = "FILTER" + + predicate { + attribute = "accumulations.policyName" + operator = "EXACTLY_MATCHES" + values = [ + for app in local.app_names : newrelic_alert_policy.rails_app_policy[app].name + ] + } + } + + destination { + channel_id = newrelic_notification_channel.slack_channel.id + } +}