diff --git a/.github/workflows/newrelic-monitors.yml b/.github/workflows/newrelic-monitors.yml new file mode 100644 index 0000000..6fe7575 --- /dev/null +++ b/.github/workflows/newrelic-monitors.yml @@ -0,0 +1,86 @@ +name: Deploy New Relic Synthetics monitors + +on: + push: + branches: + - main + paths: + - 'terraform/newrelic/**' + pull_request: + paths: + - 'terraform/newrelic/**' + # Allow manual trigger from the Actions tab + workflow_dispatch: + +jobs: + terraform: + name: Terraform ${{ github.event_name == 'pull_request' && 'Plan' || 'Apply' }} + runs-on: ubuntu-latest + defaults: + run: + working-directory: terraform/newrelic + + # Allow only one run at a time to avoid state conflicts + concurrency: + group: newrelic-terraform + cancel-in-progress: false + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + with: + terraform_version: '~1.7' + # If using HCP Terraform backend, set TF_TOKEN_app_terraform_io secret + # and uncomment the line below: + # cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }} + + - name: Terraform Init + run: terraform init + env: + TF_VAR_new_relic_account_id: ${{ secrets.NEW_RELIC_ACCOUNT_ID }} + TF_VAR_new_relic_api_key: ${{ secrets.NEW_RELIC_API_KEY }} + TF_VAR_alert_email: ${{ secrets.NEW_RELIC_ALERT_EMAIL }} + + - name: Terraform Validate + run: terraform validate + + - name: Terraform Format Check + run: terraform fmt -check -recursive + + - name: Terraform Plan + id: plan + run: terraform plan -out=tfplan -no-color + env: + TF_VAR_new_relic_account_id: ${{ secrets.NEW_RELIC_ACCOUNT_ID }} + TF_VAR_new_relic_api_key: ${{ secrets.NEW_RELIC_API_KEY }} + TF_VAR_alert_email: ${{ secrets.NEW_RELIC_ALERT_EMAIL }} + + # Post plan summary as a PR comment + - name: Comment plan on PR + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const output = `#### Terraform Plan for New Relic Monitors + \`\`\` + ${{ steps.plan.outputs.stdout }} + \`\`\` + *Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`; + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: output + }); + + # Only apply on pushes to main (not on PRs) + - name: Terraform Apply + if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' + run: terraform apply -auto-approve tfplan + env: + TF_VAR_new_relic_account_id: ${{ secrets.NEW_RELIC_ACCOUNT_ID }} + TF_VAR_new_relic_api_key: ${{ secrets.NEW_RELIC_API_KEY }} + TF_VAR_alert_email: ${{ secrets.NEW_RELIC_ALERT_EMAIL }} diff --git a/terraform/newrelic/.gitignore b/terraform/newrelic/.gitignore new file mode 100644 index 0000000..6151c07 --- /dev/null +++ b/terraform/newrelic/.gitignore @@ -0,0 +1,12 @@ +# Downloaded provider plugins — re-fetched by `tofu init` +.terraform/ + +# State files — contain sensitive resource IDs; use a remote backend for CI +terraform.tfstate +terraform.tfstate.backup +*.tfstate +*.tfstate.* + +# Local variable overrides +*.tfvars +*.tfvars.json diff --git a/terraform/newrelic/.terraform.lock.hcl b/terraform/newrelic/.terraform.lock.hcl new file mode 100644 index 0000000..0e69aad --- /dev/null +++ b/terraform/newrelic/.terraform.lock.hcl @@ -0,0 +1,28 @@ +# This file is maintained automatically by "tofu init". +# Manual edits may be lost in future updates. + +provider "registry.opentofu.org/newrelic/newrelic" { + version = "3.81.0" + constraints = "~> 3.38" + hashes = [ + "h1:Woy32ks9+zjLZEz8XH+Co4M2hQBDArmWbJiYaIPLRYU=", + "zh:0af973805868bcfe093375c5f3572cfdf1d237c28405a7bcde25a2749e392481", + "zh:144e3ac38e15115c1bb5c490d9ed88b5dcbc864d5cb4908e0211affff25fa791", + "zh:1617e14a29ae815aa0a502a23cc69463fcc15b2ae8538ecfa2dabcef2ce9c133", + "zh:445e5bed4f8ef59b602ec6471e6c6849903e4a2b63b376276fa4ca69ea18bee5", + "zh:61206498e9c22726251515f608e0e87f3d6f990b39c24915f4ac84ff78412bce", + "zh:6eaa4ca7b1b5d55f24d1dd8c4b998c17fe424b56a3cb0705d8e43e3046cb361c", + "zh:8a4975dbcbc200375d329e41a584c88d6ece58ab2db857dcfdabd86f8f9d4c19", + "zh:8f834afbb3bf30da27fc8b1b0580184c5dd832a89992d29e6b59565fa6cbcf2e", + "zh:903fbe54028f4f2c1c19dded286867b9d0c54da361215e0e944f6c7fb6e2728a", + "zh:9ed526701509a966fa1d47bf907ee8c297e94702703b05dee0d6cd1f637733e5", + "zh:d60fef0fa44c70abfba5064e789335316481ac3ca1be3a00d1434e951d4ab0fb", + "zh:d9e039c74162ab7e48444ea8a0357f411676aaa19905c55f804c84df296c2944", + "zh:dc11afea5db3ed4bb42406cc1c6725ff01f4d599b0093daa4456862aee5488f7", + "zh:e22d6ea423c7e784b20c6835d2eb57022716fbf0f25cb0cc0d2b61d5cc73ac86", + "zh:e639038f303d8de0d227d36c5f43cef27dd40bde944c218eef84c21cb3060505", + "zh:ef0f54494a0d27d55f06cf983160a743c7892d14d7d7b0539b90f4483f4369ed", + "zh:f999e4e43c9ecf75dc5300741ad473c9a27894b87606c93090736b9a7cd2874e", + "zh:fbd1fee2c9df3aa19cf8851ce134dea6e45ea01cb85695c1726670c285797e25", + ] +} diff --git a/terraform/newrelic/alerts.tf b/terraform/newrelic/alerts.tf new file mode 100644 index 0000000..06e07fe --- /dev/null +++ b/terraform/newrelic/alerts.tf @@ -0,0 +1,159 @@ +# --------------------------------------------------------------------------- +# Alert policy +# Naming convention: cai--policy- (matches cai/tf-modules pattern) +# --------------------------------------------------------------------------- +resource "newrelic_alert_policy" "contentauth_docs" { + name = "cai-${var.service}-policy-${var.environment}" + incident_preference = "PER_CONDITION" +} + +# --------------------------------------------------------------------------- +# NRQL alert condition — fires when a Synthetics monitor reports a failure +# +# Key differences from the initial version, aligned with cai/tf-modules: +# - NRQL uses entityGuid (stable entity reference) instead of monitorName +# - Adds a warning threshold (1 failure) alongside critical (5 failures) +# - Uses event_timer aggregation (60 s timer) — same as the internal module +# - violation_time_limit_seconds: 12 hours (43200 s) +# --------------------------------------------------------------------------- +resource "newrelic_nrql_alert_condition" "monitor_failure" { + account_id = var.new_relic_account_id + policy_id = newrelic_alert_policy.contentauth_docs.id + type = "static" + name = "cai-${var.service}-synthetic-failure" + description = "A Synthetics monitor detected a doc page that is down or returning non-200 (e.g. 404)." + enabled = true + + nrql { + query = <<-EOQ + SELECT count(result) + FROM SyntheticCheck + WHERE entityGuid IN ( + '${newrelic_synthetics_script_monitor.homepage.id}', + '${newrelic_synthetics_script_monitor.doc_pages_404.id}' + ) + AND result != 'SUCCESS' + FACET location, locationLabel + EOQ + } + + warning { + operator = "above" + threshold = 1 + threshold_duration = 180 # 3 minutes + threshold_occurrences = "ALL" + } + + critical { + operator = "above" + threshold = 5 + threshold_duration = 180 # 3 minutes + threshold_occurrences = "ALL" + } + + aggregation_window = 60 + aggregation_method = "event_timer" + aggregation_timer = 60 + fill_option = "none" + violation_time_limit_seconds = 43200 # 12 hours +} + +# --------------------------------------------------------------------------- +# Notification destination — email +# --------------------------------------------------------------------------- +resource "newrelic_notification_destination" "email" { + name = "cai-${var.service}-email-${var.environment}" + type = "EMAIL" + + property { + key = "email" + value = var.alert_email + } +} + +# --------------------------------------------------------------------------- +# Notification channels — critical and warning (separate, matching the module pattern) +# --------------------------------------------------------------------------- +resource "newrelic_notification_channel" "critical" { + name = "cai-${var.service}-email-critical-${var.environment}" + type = "EMAIL" + destination_id = newrelic_notification_destination.email.id + product = "IINT" + + property { + key = "subject" + value = "[CRITICAL] ContentAuth Docs alert: {{issueTitle}}" + } +} + +resource "newrelic_notification_channel" "warning" { + name = "cai-${var.service}-email-warning-${var.environment}" + type = "EMAIL" + destination_id = newrelic_notification_destination.email.id + product = "IINT" + + property { + key = "subject" + value = "[WARNING] ContentAuth Docs alert: {{issueTitle}}" + } +} + +# --------------------------------------------------------------------------- +# Workflows — separate critical and warning (matches cai/tf-modules pattern) +# Naming convention: cai--- +# --------------------------------------------------------------------------- +resource "newrelic_workflow" "critical" { + name = "cai-${var.service}-critical-${var.environment}" + muting_rules_handling = "DONT_NOTIFY_FULLY_MUTED_ISSUES" + enabled = true + + issues_filter { + name = "Filter by policy and critical priority" + type = "FILTER" + + predicate { + attribute = "labels.policyIds" + operator = "EXACTLY_MATCHES" + values = [tostring(newrelic_alert_policy.contentauth_docs.id)] + } + + predicate { + attribute = "priority" + operator = "EXACTLY_MATCHES" + values = ["CRITICAL"] + } + } + + destination { + channel_id = newrelic_notification_channel.critical.id + notification_triggers = ["ACKNOWLEDGED", "ACTIVATED", "CLOSED"] + } +} + +resource "newrelic_workflow" "warning" { + name = "cai-${var.service}-warning-${var.environment}" + muting_rules_handling = "DONT_NOTIFY_FULLY_MUTED_ISSUES" + enabled = true + + issues_filter { + name = "Filter by policy and high priority" + type = "FILTER" + + predicate { + attribute = "labels.policyIds" + operator = "EXACTLY_MATCHES" + values = [tostring(newrelic_alert_policy.contentauth_docs.id)] + } + + predicate { + attribute = "priority" + operator = "EXACTLY_MATCHES" + values = ["HIGH"] + } + } + + destination { + channel_id = newrelic_notification_channel.warning.id + notification_triggers = ["ACKNOWLEDGED", "ACTIVATED", "CLOSED"] + } +} diff --git a/terraform/newrelic/dashboard.tf b/terraform/newrelic/dashboard.tf new file mode 100644 index 0000000..97bb4cf --- /dev/null +++ b/terraform/newrelic/dashboard.tf @@ -0,0 +1,144 @@ +# --------------------------------------------------------------------------- +# New Relic One dashboard — ContentAuth Docs site health +# NRQL uses entityGuid for stable monitor references (aligned with cai/tf-modules) +# --------------------------------------------------------------------------- +resource "newrelic_one_dashboard" "contentauth_docs" { + name = "ContentAuth Docs — Site Health (${var.environment})" + permissions = "public_read_only" + + page { + name = "Site Health" + + # Row 1 — KPI billboards + widget_billboard { + title = "Success Rate (24 h)" + row = 1 + column = 1 + width = 4 + height = 3 + + nrql_query { + account_id = var.new_relic_account_id + query = <<-EOQ + SELECT percentage(count(*), WHERE result = 'SUCCESS') AS 'Success Rate' + FROM SyntheticCheck + WHERE entityGuid IN ( + '${newrelic_synthetics_script_monitor.homepage.id}', + '${newrelic_synthetics_script_monitor.doc_pages_404.id}' + ) + SINCE 24 hours ago + EOQ + } + + warning = 99 + critical = 95 + } + + widget_billboard { + title = "Failed Checks (24 h)" + row = 1 + column = 5 + width = 4 + height = 3 + + nrql_query { + account_id = var.new_relic_account_id + query = <<-EOQ + SELECT count(*) AS 'Failures' + FROM SyntheticCheck + WHERE entityGuid IN ( + '${newrelic_synthetics_script_monitor.homepage.id}', + '${newrelic_synthetics_script_monitor.doc_pages_404.id}' + ) + AND result != 'SUCCESS' + SINCE 24 hours ago + EOQ + } + + critical = 1 + } + + widget_billboard { + title = "Avg Response Time (24 h)" + row = 1 + column = 9 + width = 4 + height = 3 + + nrql_query { + account_id = var.new_relic_account_id + query = <<-EOQ + SELECT average(duration) AS 'Avg Duration (ms)' + FROM SyntheticCheck + WHERE entityGuid IN ( + '${newrelic_synthetics_script_monitor.homepage.id}', + '${newrelic_synthetics_script_monitor.doc_pages_404.id}' + ) + SINCE 24 hours ago + EOQ + } + + warning = 3000 + critical = 8000 + } + + # Row 4 — success rate trend per monitor + widget_line { + title = "Monitor Success Rate — 7 days" + row = 4 + column = 1 + width = 12 + height = 3 + + nrql_query { + account_id = var.new_relic_account_id + query = <<-EOQ + SELECT percentage(count(*), WHERE result = 'SUCCESS') AS 'Success %' + FROM SyntheticCheck + WHERE entityGuid IN ( + '${newrelic_synthetics_script_monitor.homepage.id}', + '${newrelic_synthetics_script_monitor.doc_pages_404.id}' + ) + FACET monitorName + TIMESERIES AUTO + SINCE 7 days ago + EOQ + } + } + + # Row 7 — failure table (most useful for triaging 404 incidents) + widget_table { + title = "Recent Monitor Failures" + row = 7 + column = 1 + width = 12 + height = 5 + + nrql_query { + account_id = var.new_relic_account_id + query = <<-EOQ + SELECT + timestamp, + monitorName, + result, + duration, + locationLabel, + error + FROM SyntheticCheck + WHERE entityGuid IN ( + '${newrelic_synthetics_script_monitor.homepage.id}', + '${newrelic_synthetics_script_monitor.doc_pages_404.id}' + ) + AND result != 'SUCCESS' + SINCE 7 days ago + LIMIT 100 + EOQ + } + } + } +} + +output "dashboard_url" { + description = "URL of the New Relic site health dashboard" + value = "https://one.newrelic.com/dashboards/${newrelic_one_dashboard.contentauth_docs.guid}" +} diff --git a/terraform/newrelic/main.tf b/terraform/newrelic/main.tf new file mode 100644 index 0000000..de8dbba --- /dev/null +++ b/terraform/newrelic/main.tf @@ -0,0 +1,32 @@ +terraform { + required_version = ">= 1.3" + + required_providers { + newrelic = { + source = "newrelic/newrelic" + version = "~> 3.38" + } + } + + # Recommended: configure a remote backend so state persists across CI runs. + # This team uses an S3 backend with DynamoDB state locking — see cr-web/alerts/main.tf + # for the reference pattern. + # + # backend "s3" { + # bucket = "" + # dynamodb_table = "" + # region = "us-east-1" + # workspace_key_prefix = "opensource-docs/alerts" + # key = "terraform.tfstate" + # encrypt = true + # } + # + # Without a remote backend, `terraform apply` in CI will recreate resources + # on every run. Configure the backend above before running in GitHub Actions. +} + +provider "newrelic" { + account_id = var.new_relic_account_id + api_key = var.new_relic_api_key + region = var.new_relic_region +} diff --git a/terraform/newrelic/monitors.tf b/terraform/newrelic/monitors.tf new file mode 100644 index 0000000..fd952be --- /dev/null +++ b/terraform/newrelic/monitors.tf @@ -0,0 +1,105 @@ +# Locations used by all monitors. +# Matches the production location set used in cai/tf-modules new-relic-alerts module. +locals { + # NR API stores location names without the AWS_ prefix — use short form to avoid + # perpetual drift between config and state on every plan. + monitor_locations = [ + "AP_EAST_1", + "AP_SOUTH_1", + "AP_SOUTHEAST_1", + "AP_NORTHEAST_1", + "AP_NORTHEAST_2", + "AP_SOUTHEAST_2", + "US_WEST_1", + "US_WEST_2", + "US_EAST_1", + "US_EAST_2", + "CA_CENTRAL_1", + "SA_EAST_1", + "EU_WEST_1", + "EU_WEST_2", + "EU_WEST_3", + "EU_CENTRAL_1", + "EU_NORTH_1", + "EU_SOUTH_1", + "ME_SOUTH_1", + "AF_SOUTH_1", + ] +} + +# --------------------------------------------------------------------------- +# SCRIPT_BROWSER monitor — homepage content check +# Uses Selenium ($webDriver) to load the homepage and assert that: +# - The page does NOT show "Page Not Found" / 404 +# - Navigation and main content elements are rendered +# Naming convention: cai-- (matches cai/tf-modules pattern) +# --------------------------------------------------------------------------- +resource "newrelic_synthetics_script_monitor" "homepage" { + name = "cai-${var.service}-${var.environment}" + type = "SCRIPT_BROWSER" + period = "EVERY_15_MINUTES" + status = "ENABLED" + + runtime_type = "CHROME_BROWSER" + runtime_type_version = "100" + script_language = "JAVASCRIPT" + + locations_public = local.monitor_locations + + script = templatefile("${path.module}/scripts/homepage_health_check.js.tftpl", { + TEST_FQDN = "'${var.site_fqdn}'" + }) + + tag { + key = "service" + values = [var.service] + } + + tag { + key = "env" + values = [var.environment] + } + + tag { + key = "team" + values = ["CAI"] + } +} + +# --------------------------------------------------------------------------- +# SCRIPT_API monitor — checks multiple key doc pages for 404s +# Uses $http (not a browser) to rapidly check HTTP status across 7 URLs. +# This directly targets the reported issue: search results pointing to 404 pages. +# Naming convention: cai--page-health- +# --------------------------------------------------------------------------- +resource "newrelic_synthetics_script_monitor" "doc_pages_404" { + name = "cai-${var.service}-page-health-${var.environment}" + type = "SCRIPT_API" + period = "EVERY_15_MINUTES" + status = "ENABLED" + + runtime_type = "NODE_API" + runtime_type_version = "16.10" + script_language = "JAVASCRIPT" + + locations_public = local.monitor_locations + + script = templatefile("${path.module}/scripts/doc_pages_404_check.js.tftpl", { + TEST_FQDN = "'${var.site_fqdn}'" + }) + + tag { + key = "service" + values = [var.service] + } + + tag { + key = "env" + values = [var.environment] + } + + tag { + key = "team" + values = ["CAI"] + } +} diff --git a/terraform/newrelic/scripts/doc_pages_404_check.js.tftpl b/terraform/newrelic/scripts/doc_pages_404_check.js.tftpl new file mode 100644 index 0000000..ff76e1c --- /dev/null +++ b/terraform/newrelic/scripts/doc_pages_404_check.js.tftpl @@ -0,0 +1,72 @@ +/* eslint-disable no-undef */ +/* eslint-disable no-console */ + +// -------------------- CONSTANTS +const SCRIPT_NAME = 'ContentAuth Docs — Doc Pages 404 Check'; +const BASE_URL = ${TEST_FQDN}; // Terraform templatefile injects quoted URL + +// -------------------- DEPENDENCIES +var assert = require('assert'); + +// -------------------- PAGES TO CHECK +// Add or remove paths here as documentation structure changes. +// These were identified as likely to break when search results point to stale URLs. +var pages = [ + { path: '', label: 'Homepage' }, + { path: 'docs/introduction', label: 'Introduction' }, + { path: 'docs/getting-started/', label: 'Getting started' }, + { path: 'docs/js-sdk/js-landing', label: 'JS SDK overview' }, + { path: 'docs/rust-sdk/', label: 'Rust SDK overview' }, + { path: 'docs/c2pa-python/', label: 'Python SDK overview' }, + { path: 'docs/node-landing', label: 'Node.js SDK overview' }, +]; + +var requestOptions = { + followAllRedirects: true, + timeout: 15000, + headers: { + 'User-Agent': 'NewRelic-Synthetics/contentauth-docs-404-check', + }, +}; + +function checkPage(page) { + var url = BASE_URL + page.path; + return new Promise(function (resolve, reject) { + $http.get( + Object.assign({ uri: url }, requestOptions), + function (err, response) { + if (err) { + reject( + new Error( + 'Network error fetching "' + page.label + '" (' + url + '): ' + err.message, + ), + ); + return; + } + + if (response.statusCode !== 200) { + reject( + new Error( + '"' + page.label + '" returned HTTP ' + response.statusCode + + ' (expected 200). URL: ' + url, + ), + ); + return; + } + + console.log('[OK] ' + page.label + ' — HTTP ' + response.statusCode); + resolve(); + }, + ); + }); +} + +// Check all pages; a single non-200 response fails the monitor +Promise.all(pages.map(checkPage)) + .then(function () { + console.log('✓ All ' + pages.length + ' pages returned HTTP 200'); + }) + .catch(function (err) { + console.error('❌ ' + err.message); + throw err; + }); diff --git a/terraform/newrelic/scripts/homepage_health_check.js.tftpl b/terraform/newrelic/scripts/homepage_health_check.js.tftpl new file mode 100644 index 0000000..3f568ce --- /dev/null +++ b/terraform/newrelic/scripts/homepage_health_check.js.tftpl @@ -0,0 +1,82 @@ +/* eslint-disable no-undef */ +/* eslint-disable no-console */ + +// -------------------- CONSTANTS +const SCRIPT_NAME = 'ContentAuth Docs — Homepage Health Check'; +const PAGE_LOAD_TIMEOUT = 30000; // 30 seconds +const SCRIPT_TIMEOUT = 30000; // 30 seconds +const SITE_URL = ${TEST_FQDN}; // Terraform templatefile injects quoted URL + +// -------------------- DEPENDENCIES +const assert = require('assert'); + +// -------------------- CONFIGURATION +await $webDriver.manage().setTimeouts({ + pageLoad: PAGE_LOAD_TIMEOUT, + script: SCRIPT_TIMEOUT, +}); + +const By = $selenium.By; +const until = $selenium.until; + +// -------------------- SCRIPT +const startTime = new Date(); +console.log('Starting: ' + SCRIPT_NAME); +console.log('Target: ' + SITE_URL); + +try { + await $webDriver.get(SITE_URL); + + // Wait for page body to confirm the server responded + await $webDriver.wait( + until.elementLocated(By.tagName('body')), + 10000, + 'Page body not found — server may not have responded', + ); + + // Check page title: must not be a 404 / "Page Not Found" + const title = await $webDriver.getTitle(); + console.log('Page title: ' + title); + + assert.ok( + !title.toLowerCase().includes('page not found') && + !title.includes('404'), + 'Homepage returned a 404 page. Title: ' + title, + ); + + assert.ok( + title.includes('Content Auth') || + title.includes('content authenticity') || + title.includes('CAI') || + title.includes('Docs') || + title.includes('Open Source') || + title.includes('Open-source'), + 'Unexpected homepage title (may indicate wrong page loaded): ' + title, + ); + + // Check navigation / header element exists and is visible + const navElement = await $webDriver.wait( + until.elementLocated(By.css('nav, [role="navigation"], header')), + 5000, + 'Navigation / header element not found', + ); + const navVisible = await navElement.isDisplayed(); + assert.equal(true, navVisible, 'Navigation element is not visible'); + console.log('✓ Navigation element found and visible'); + + // Check main content wrapper (Docusaurus renders .main-wrapper) + await $webDriver.wait( + until.elementLocated(By.css('main, [role="main"], .main-wrapper')), + 5000, + 'Main content area not found — page may have rendered an error state', + ); + console.log('✓ Main content area found'); + +} catch (error) { + console.error('❌ Error in ' + SCRIPT_NAME + ': ' + error.message); + throw error; // Re-throw so New Relic records a FAILED result +} + +const duration = (new Date() - startTime) / 1000; +console.log('Duration: ' + duration + 's'); +console.log('✓ ' + SCRIPT_NAME + ' completed successfully'); diff --git a/terraform/newrelic/variables.tf b/terraform/newrelic/variables.tf new file mode 100644 index 0000000..f87eaae --- /dev/null +++ b/terraform/newrelic/variables.tf @@ -0,0 +1,44 @@ +variable "service" { + description = "Kebab-case service name — used in resource naming convention cai--" + type = string + default = "opensource-docs" +} + +variable "new_relic_account_id" { + description = "New Relic account ID (NR UI → Account settings)" + type = number +} + +variable "new_relic_api_key" { + description = "New Relic User API key (NR UI → API Keys → create a User key)" + type = string + sensitive = true +} + +variable "new_relic_region" { + description = "New Relic data center region: US or EU" + type = string + default = "US" + + validation { + condition = contains(["US", "EU"], var.new_relic_region) + error_message = "Region must be either US or EU." + } +} + +variable "alert_email" { + description = "Email address to receive Synthetics failure alerts" + type = string +} + +variable "site_fqdn" { + description = "Base URL of the documentation site (trailing slash required)" + type = string + default = "https://opensource.contentauthenticity.org/" +} + +variable "environment" { + description = "Deployment environment name — used in resource naming (e.g. prod)" + type = string + default = "prod" +}