From abcbf9c0dd3474bd0dcb4b72608edc69467161e4 Mon Sep 17 00:00:00 2001 From: Anmol Nagpal Date: Thu, 16 Mar 2023 21:26:47 +0530 Subject: [PATCH] Added tf module for aws synthetic canaries --- .github/workflows/readme.yaml | 54 +++++ .github/workflows/semantic-releaser.yml | 30 --- .../{static-checks.yml => terraform.yaml} | 16 +- .github/workflows/terratest.yaml | 42 ++++ .github/workflows/tfsec.yaml | 25 +++ .gitignore | 8 + .pre-commit-config.yaml | 2 +- Makefile | 3 + README.md | 165 +++++++++++++- README.yaml | 56 +++++ _example/README.md | 3 - _example/complete/README.md | 0 _example/complete/main.tf | 15 +- _example/complete/outputs.tf | 0 _example/complete/variables.auto.tfvars | 0 _example/complete/variables.tf | 0 _test/complete/watch_test.go | 31 +++ main.tf | 201 +++++++++++++++++- variables.tf | 73 +++++++ 19 files changed, 684 insertions(+), 40 deletions(-) create mode 100644 .github/workflows/readme.yaml delete mode 100644 .github/workflows/semantic-releaser.yml rename .github/workflows/{static-checks.yml => terraform.yaml} (80%) create mode 100644 .github/workflows/terratest.yaml create mode 100644 .github/workflows/tfsec.yaml create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.yaml delete mode 100644 _example/README.md delete mode 100644 _example/complete/README.md delete mode 100644 _example/complete/outputs.tf delete mode 100644 _example/complete/variables.auto.tfvars delete mode 100644 _example/complete/variables.tf create mode 100644 _test/complete/watch_test.go diff --git a/.github/workflows/readme.yaml b/.github/workflows/readme.yaml new file mode 100644 index 0000000..dd688ac --- /dev/null +++ b/.github/workflows/readme.yaml @@ -0,0 +1,54 @@ +name: 'Create README.md file' +on: + push: + branches: + - master + +jobs: + readme-create: + name: 'readme-create' + runs-on: ubuntu-latest + steps: + - name: 'Checkout' + uses: actions/checkout@master + + - name: Set up Python 3.7. + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: 'create readme' + uses: 'clouddrove/github-actions@v9.0.2' + with: + actions_subcommand: 'readme' + github_token: '${{ secrets.GITHUB}}' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN}} + + + - name: 'pre-commit check errors' + uses: pre-commit/action@v2.0.0 + continue-on-error: true + + - name: 'pre-commit fix erros' + uses: pre-commit/action@v2.0.0 + continue-on-error: true + + - name: 'push readme' + uses: 'clouddrove/github-actions@v9.0.2' + continue-on-error: true + with: + actions_subcommand: 'push' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN}} + + - name: 'Slack Notification' + uses: clouddrove/action-slack@v2 + with: + status: ${{ job.status }} + fields: repo,author + author_name: 'CloudDrove' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # required + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_TERRAFORM }} # required + if: always() \ No newline at end of file diff --git a/.github/workflows/semantic-releaser.yml b/.github/workflows/semantic-releaser.yml deleted file mode 100644 index 6e685a0..0000000 --- a/.github/workflows/semantic-releaser.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Release - -on: - push: - branches: - - main - paths: - - '**.tf' - - '!examples/**.tf' - -jobs: - release: - name: Release - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Setup Node.js - uses: actions/setup-node@v1 - with: - node-version: 14 - - - name: Release - env: - GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }} - run: npx semantic-release diff --git a/.github/workflows/static-checks.yml b/.github/workflows/terraform.yaml similarity index 80% rename from .github/workflows/static-checks.yml rename to .github/workflows/terraform.yaml index 08638e3..10e9868 100644 --- a/.github/workflows/static-checks.yml +++ b/.github/workflows/terraform.yaml @@ -19,6 +19,7 @@ jobs: minVersion: ${{ steps.minMax.outputs.minVersion }} maxVersion: ${{ steps.minMax.outputs.maxVersion }} + versionEvaluate: name: Evaluate Terraform versions runs-on: ubuntu-latest @@ -30,7 +31,7 @@ jobs: - ${{ needs.versionExtract.outputs.minVersion }} - ${{ needs.versionExtract.outputs.maxVersion }} directory: - - _example/complete + - _example/ steps: - name: Checkout @@ -41,15 +42,24 @@ jobs: with: terraform_version: ${{ matrix.version }} + - name: 'Configure AWS Credentials' + uses: clouddrove/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.TEST_AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.TEST_AWS_ACCESS_SECRET_KEY }} + aws-region: us-east-2 + - name: Init & validate v${{ matrix.version }} run: | cd ${{ matrix.directory }} terraform init terraform validate + - name: tflint uses: reviewdog/action-tflint@master with: - github_token: ${{ secrets.GITHUB_TOKEN }} + tflint_version: v0.29.0 + github_token: ${{ secrets.GITHUB }} working_directory: ${{ matrix.directory }} fail_on_error: 'true' filter_mode: 'nofilter' @@ -70,4 +80,4 @@ jobs: terraform_version: ${{ needs.versionExtract.outputs.maxVersion }} - name: Check Terraform format changes - run: terraform fmt --recursive -check=true + run: terraform fmt --recursive \ No newline at end of file diff --git a/.github/workflows/terratest.yaml b/.github/workflows/terratest.yaml new file mode 100644 index 0000000..014919e --- /dev/null +++ b/.github/workflows/terratest.yaml @@ -0,0 +1,42 @@ +name: 'Terratest GitHub Actions' +on: + pull_request: + branches: + - master + types: [labeled] + +jobs: + terraform: + name: 'Terraform' + runs-on: ubuntu-latest + steps: + + - name: 'Checkout' + uses: actions/checkout@master + + - name: Configure AWS Credentials + uses: clouddrove/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.TEST_AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.TEST_AWS_ACCESS_SECRET_KEY }} + aws-region: us-east-2 + + - name: 'Terratest anomaly_example' + if: ${{ github.event.label.name == 'terratest' }} + uses: 'clouddrove/github-actions@v9.0.2' + with: + actions_subcommand: 'terratest' + tf_actions_working_dir: '_test/complete' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: 'Slack Notification' + uses: clouddrove/action-slack@v2 + with: + status: ${{ job.status }} + fields: repo,author + author_name: 'CloudDrove' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # required + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_TERRAFORM }} # required + if: always() \ No newline at end of file diff --git a/.github/workflows/tfsec.yaml b/.github/workflows/tfsec.yaml new file mode 100644 index 0000000..20cde2d --- /dev/null +++ b/.github/workflows/tfsec.yaml @@ -0,0 +1,25 @@ +name: tfsec +on: + pull_request: + +jobs: + tfsec: + name: tfsec sarif report + runs-on: ubuntu-latest + + steps: + - name: Clone repo + uses: actions/checkout@master + + - name: tfsec + uses: aquasecurity/tfsec-sarif-action@v0.1.0 + with: + sarif_file: tfsec.sarif + working_directory: _example + full_repo_scan: true + + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v1 + with: + # Path to SARIF file relative to the root of the repository + sarif_file: tfsec.sarif \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46e3cbd --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# ignored files +*.tfstate +*.tfstate.backup +.terraform +.idea +*.iml +go.sum +*.terraform.lock.hcl \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9e713a5..1b5f59d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,4 +18,4 @@ repos: - id: check-merge-conflict - id: debug-statements - id: check-yaml - - id: check-added-large-files + - id: check-added-large-files \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d9e69c6 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +export GENIE_PATH ?= $(shell 'pwd')/../../../genie + +include $(GENIE_PATH)/Makefile \ No newline at end of file diff --git a/README.md b/README.md index d0ee71b..00942c1 100644 --- a/README.md +++ b/README.md @@ -1 +1,164 @@ -# terraform-module-template \ No newline at end of file + + +

+ + +

+ Terraform AWS Cloudwatch Alarms +

+ +

+ Terraform module creates Cloudwatch Alarm on AWS for monitoriing AWS services. +

+ +

+ + + Terraform + + + Licence + + + tfsec + + + static-checks + + + +

+

+ + + + + + + + + + + +

+
+ + +We eat, drink, sleep and most importantly love **DevOps**. We are working towards strategies for standardizing architecture while ensuring security for the infrastructure. We are strong believer of the philosophy Bigger problems are always solved by breaking them into smaller manageable problems. Resonating with microservices architecture, it is considered best-practice to run database, cluster, storage in smaller connected yet manageable pieces within the infrastructure. + +This module is basically combination of [Terraform open source](https://www.terraform.io/) and includes automatation tests and examples. It also helps to create and improve your infrastructure with minimalistic code instead of maintaining the whole infrastructure code yourself. + +We have [*fifty plus terraform modules*][terraform_modules]. A few of them are comepleted and are available for open source usage while a few others are in progress. + + + + +## Prerequisites + +This module has a few dependencies: + +- [Terraform 1.x.x](https://learn.hashicorp.com/terraform/getting-started/install.html) +- [Go](https://golang.org/doc/install) +- [github.com/stretchr/testify/assert](https://github.com/stretchr/testify) +- [github.com/gruntwork-io/terratest/modules/terraform](https://github.com/gruntwork-io/terratest) + + + + + + + +## Examples + + +**IMPORTANT:** Since the `master` branch used in `source` varies based on new modifications, we suggest that you use the release versions [here](https://github.com/clouddrove/terraform-aws-cloudwatch-alarms/releases). + + +Here are some examples of how you can use this module in your inventory structure: +### Example +```hcl + module "canaries" { + name = "canary" + environment = "test" + source = "../.." + schedule_expression = "rate(5 minutes)" + s3_artifact_bucket = "my-test-artifact-bucket" # must pre-exist + alarm_email = "test.user@clouddrove.com" # you need to confirm this email address + endpoints = { "test-example" = { url = "https://example.com" } } + subnet_ids = module.subnets.private_subnet_id + security_group_ids = [module.ssh.security_group_ids] + } +``` + + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| actions\_enabled | Indicates whether or not actions should be executed during any changes to the alarm's state. | `bool` | `true` | no | +| alarm\_actions | The list of actions to execute when this alarm transitions into an ALARM state from any other state. | `list(any)` | `[]` | no | +| alarm\_description | The description for the alarm. | `string` | `""` | no | +| alarm\_name | The descriptive name for the alarm. | `string` | n/a | yes | +| comparison\_operator | The arithmetic operation to use when comparing the specified Statistic and Threshold. | `string` | n/a | yes | +| dimensions | Dimensions for metrics. | `map` | `{}` | no | +| enabled | Enable alarm. | `bool` | `true` | no | +| environment | Environment (e.g. `prod`, `dev`, `staging`). | `string` | `""` | no | +| evaluation\_periods | The number of periods over which data is compared to the specified threshold. | `number` | n/a | yes | +| expression\_enabled | Enable alarm with expression. | `bool` | `false` | no | +| instance\_id | The instance ID. | `string` | `""` | no | +| insufficient\_data\_actions | The list of actions to execute when this alarm transitions into an INSUFFICIENT\_DATA state from any other state. | `list(any)` | `[]` | no | +| label\_order | Label order, e.g. `name`,`application`. | `list(any)` | `[]` | no | +| managedby | ManagedBy, eg 'CloudDrove'. | `string` | `"hello@clouddrove.com"` | no | +| metric\_name | The name for the alarm's associated metric. | `string` | `"CPUUtilization"` | no | +| name | Name (e.g. `app` or `cluster`). | `string` | `""` | no | +| namespace | The namespace for the alarm's associated metric. | `string` | `"AWS/EC2"` | no | +| ok\_actions | The list of actions to execute when this alarm transitions into an OK state from any other state. | `list(any)` | `[]` | no | +| period | The period in seconds over which the specified statistic is applied. | `number` | `120` | no | +| query\_expressions | values for metric query expression. | `list` |
[
{
"expression": "ANOMALY_DETECTION_BAND(m1)",
"id": "e1",
"label": "CPUUtilization (Expected)",
"return_data": "true"
}
]
| no | +| query\_metrics | values for metric query metrics. | `list` |
[
{
"dimensions": {
"InstanceId": "i-abc123"
},
"id": "m1",
"metric_name": "CPUUtilization",
"namespace": "AWS/EC2",
"period": "120",
"return_data": "true",
"stat": "Average",
"unit": "Count"
}
]
| no | +| repository | Terraform current module repo | `string` | `"https://github.com/clouddrove/terraform-aws-cloudwatch-alarms"` | no | +| statistic | The statistic to apply to the alarm's associated metric. | `string` | `"Average"` | no | +| threshold | The value against which the specified statistic is compared. | `number` | `40` | no | +| threshold\_metric\_id | If this is an alarm based on an anomaly detection model, make this value match the ID of the ANOMALY\_DETECTION\_BAND function. | `string` | `""` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| arn | The ARN of the cloudwatch metric alarm. | +| id | The ID of the health check. | +| tags | A mapping of tags to assign to the resource. | + + + + +## Testing +In this module testing is performed with [terratest](https://github.com/gruntwork-io/terratest) and it creates a small piece of infrastructure, matches the output like ARN, ID and Tags name etc and destroy infrastructure in your AWS account. This testing is written in GO, so you need a [GO environment](https://golang.org/doc/install) in your system. + +You need to run the following command in the testing folder: +```hcl + go test -run Test +``` + + + +## Feedback +If you come accross a bug or have any feedback, please log it in our [issue tracker](https://github.com/clouddrove/terraform-aws-cloudwatch-alarms/issues), or feel free to drop us an email at [hello@clouddrove.com](mailto:hello@clouddrove.com). + +If you have found it worth your time, go ahead and give us a ★ on [our GitHub](https://github.com/clouddrove/terraform-aws-cloudwatch-alarms)! + +## About us + +At [CloudDrove][website], we offer expert guidance, implementation support and services to help organisations accelerate their journey to the cloud. Our services include docker and container orchestration, cloud migration and adoption, infrastructure automation, application modernisation and remediation, and performance engineering. + +

We are The Cloud Experts!

+
+

We ❤️ Open Source and you can check out our other modules to get help with your new Cloud ideas.

+ + [website]: https://clouddrove.com + [github]: https://github.com/clouddrove + [linkedin]: https://cpco.io/linkedin + [twitter]: https://twitter.com/clouddrove/ + [email]: https://clouddrove.com/contact-us.html + [terraform_modules]: https://github.com/clouddrove?utf8=%E2%9C%93&q=terraform-&type=&language= diff --git a/README.yaml b/README.yaml new file mode 100644 index 0000000..3f260ff --- /dev/null +++ b/README.yaml @@ -0,0 +1,56 @@ +--- +# +# This is the canonical configuration for the `README.md` +# Run `make readme` to rebuild the `README.md` +# + +# Name of this project +name: Terraform AWS Cloudwatch Alarms + +# License of this project +license: "APACHE" + +# Canonical GitHub repo +github_repo: clouddrove/terraform-aws-cloudwatch-alarms + +# Badges to display +badges: + - name: "Terraform" + image: "https://img.shields.io/badge/Terraform-v1.1.7-green" + url: "https://www.terraform.io" + - name: "Licence" + image: "https://img.shields.io/badge/License-APACHE-blue.svg" + url: "LICENSE.md" + - name: "tfsec" + image: "https://github.com/clouddrove/terraform-aws-cloudwatch-alarms/actions/workflows/tfsec.yml/badge.svg" + url: "https://github.com/clouddrove/terraform-aws-cloudwatch-alarms/actions/workflows/tfsec.yml" + - name: "static-checks" + image: "https://github.com/clouddrove/terraform-aws-cloudwatch-alarms/actions/workflows/terraform.yml/badge.svg" + url: "https://github.com/clouddrove/terraform-aws-cloudwatch-alarms/actions/workflows/terraform.yml" + +# description of this project +description: |- + Terraform module creates Cloudwatch Alarm on AWS for monitoriing AWS services. + +# extra content +include: + - "terraform.md" + +# How to use this project +# yamllint disable rule:line-length +usage: |- + Here are some examples of how you can use this module in your inventory structure: + ### Basic Example + ```hcl + module "canaries" { + name = "canary" + environment = "test" + source = "../.." + schedule_expression = "rate(5 minutes)" + s3_artifact_bucket = "my-test-artifact-bucket" # must pre-exist + alarm_email = "test.user@clouddrove.com" # you need to confirm this email address + endpoints = { "test-example" = { url = "https://example.com" } } + subnet_ids = module.subnets.private_subnet_id + security_group_ids = [module.ssh.security_group_ids] + } + ``` diff --git a/_example/README.md b/_example/README.md deleted file mode 100644 index 6e809ee..0000000 --- a/_example/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Terraform examples - -- [Complete](./complete) diff --git a/_example/complete/README.md b/_example/complete/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/_example/complete/main.tf b/_example/complete/main.tf index 066066c..9886e6f 100644 --- a/_example/complete/main.tf +++ b/_example/complete/main.tf @@ -1 +1,14 @@ -locals {} +provider "aws" { + region = "us-east-1" +} +module "canaries" { + name = "canary" + environment = "test" + source = "../.." + schedule_expression = "rate(5 minutes)" + s3_artifact_bucket = "test-bucket" # must pre-exist + alarm_email = "test.user@clouddrove.com" # you need to confirm this email address + endpoints = { "test-example" = { url = "https://example.com" } } + subnet_ids = module.subnets.private_subnet_id + security_group_ids = [module.ssh.security_group_ids] +} \ No newline at end of file diff --git a/_example/complete/outputs.tf b/_example/complete/outputs.tf deleted file mode 100644 index e69de29..0000000 diff --git a/_example/complete/variables.auto.tfvars b/_example/complete/variables.auto.tfvars deleted file mode 100644 index e69de29..0000000 diff --git a/_example/complete/variables.tf b/_example/complete/variables.tf deleted file mode 100644 index e69de29..0000000 diff --git a/_test/complete/watch_test.go b/_test/complete/watch_test.go new file mode 100644 index 0000000..896d66b --- /dev/null +++ b/_test/complete/watch_test.go @@ -0,0 +1,31 @@ +// Managed By : CloudDrove +// Description : This Terratest is used to test the Terraform cloudwatch-alarm module. +// Copyright @ CloudDrove. All Right Reserved. +package test + +import ( + "testing" + "github.com/gruntwork-io/terratest/modules/terraform" + "github.com/stretchr/testify/assert" +) + +func TestCloudWatch(t *testing.T) { + t.Parallel() + + terraformOptions := &terraform.Options{ + // Source path of Terraform directory. + TerraformDir: "../../_example/complete", + } + + // This will run `terraform init` and `terraform apply` and fail the test if there are any errors + terraform.InitAndApply(t, terraformOptions) + + // To clean up any resources that have been created, run 'terraform destroy' towards the end of the test + defer terraform.Destroy(t, terraformOptions) + + // To get the value of an output variable, run 'terraform output' + Tags := terraform.OutputMap(t, terraformOptions, "tags") + + // Check that we get back the outputs that we expect + assert.Equal(t, "aws-cloudwatch-synthetic", Tags["Name"]) +} \ No newline at end of file diff --git a/main.tf b/main.tf index 066066c..f320a3a 100644 --- a/main.tf +++ b/main.tf @@ -1 +1,200 @@ -locals {} +# Managed By : CloudDrove +# Description : This Script is used to create Cloudwatch Alarms. +# Copyright @ CloudDrove. All Right Reserved. + +#Module : Label +#Description : This terraform module is designed to generate consistent label names and tags +# for resources. You can use terraform-labels to implement a strict naming +# convention. +module "labels" { + source = "clouddrove/labels/aws" + version = "1.3.0" + + name = var.name + environment = var.environment + repository = var.repository + managedby = var.managedby + label_order = var.label_order +} + +#Module : CLOUDWATCH SYNTHETIC CANARY +#Description : Terraform module creates Cloudwatch Synthetic canaries on AWS for monitoriing Websites. + +locals { + file_content = { for k, v in var.endpoints : + k => templatefile("${path.module}/canary-lambda.js.tpl", { + endpoint = v.url + }) + } +} + +data "archive_file" "canary_archive_file" { + for_each = var.endpoints + type = "zip" + output_path = "/tmp/${each.key}-${md5(local.file_content[each.key])}.zip" + + source { + content = local.file_content[each.key] + filename = "nodejs/node_modules/pageLoadBlueprint.js" + } +} + +resource "aws_synthetics_canary" "canary" { + for_each = var.endpoints + name = each.key + artifact_s3_location = "s3://${var.s3_artifact_bucket}/${each.key}" + execution_role_arn = aws_iam_role.canary_role.arn + handler = "pageLoadBlueprint.handler" + zip_file = "/tmp/${each.key}-${md5(local.file_content[each.key])}.zip" + runtime_version = "syn-nodejs-puppeteer-3.9" + start_canary = true + tags = module.labels.tags + + schedule { + expression = var.schedule_expression + } + + vpc_config { + subnet_ids = var.subnet_ids + security_group_ids = var.security_group_ids + } + + depends_on = [data.archive_file.canary_archive_file, aws_iam_role_policy_attachment.canary_role_policy] +} + +#Module : IAM ROLE FOR AWS SYNTHETIC CANARY +#Description : Terraform module creates IAM Role for Cloudwatch Synthetic canaries on AWS for monitoriing Websites. + +resource "aws_iam_policy" "canary_policy" { + name = "canary-policy" + description = "Policy for canary" + policy = data.aws_iam_policy_document.canary_permissions.json +} + +#tfsec:ignore:aws-iam-no-policy-wildcards +data "aws_iam_policy_document" "canary_permissions" { + statement { + effect = "Allow" + actions = [ + "s3:PutObject", + "s3:GetObject" + ] + resources = [ + "arn:aws:s3:::${var.s3_artifact_bucket}/*" + ] + } + statement { + effect = "Allow" + actions = [ + "s3:GetBucketLocation" + ] + resources = [ + "arn:aws:s3:::${var.s3_artifact_bucket}" + ] + } + statement { + effect = "Allow" + actions = [ + "logs:CreateLogStream", + "logs:PutLogEvents", + "logs:CreateLogGroup" + ] + resources = [ + "arn:aws:logs:*:*:log-group:/aws/lambda/cwsyn-*" + ] + } + statement { + effect = "Allow" + actions = [ + "s3:ListAllMyBuckets", + "xray:PutTraceSegments" + ] + resources = [ + "*" + ] + } + statement { + effect = "Allow" + resources = [ + "*" + ] + actions = [ + "cloudwatch:PutMetricData" + ] + condition { + test = "StringEquals" + variable = "cloudwatch:namespace" + values = [ + "CloudWatchSynthetics" + ] + } + } + statement { + effect = "Allow" + actions = [ + "ec2:CreateNetworkInterface", + "ec2:DescribeNetworkInterfaces", + "ec2:DeleteNetworkInterface" + ] + resources = [ + "*" + ] + } +} + +resource "aws_iam_role" "canary_role" { + name = "CloudWatchSyntheticsRole" + assume_role_policy = data.aws_iam_policy_document.canary_assume_role.json +} + +data "aws_iam_policy_document" "canary_assume_role" { + statement { + actions = ["sts:AssumeRole"] + principals { + type = "Service" + identifiers = ["lambda.amazonaws.com"] + } + } +} + +resource "aws_iam_role_policy_attachment" "canary_role_policy" { + role = aws_iam_role.canary_role.name + policy_arn = aws_iam_policy.canary_policy.arn +} + +#Module : CLOUDWATCH ALARM FOR AWS SYNTHETIC CANARY +#Description : Terraform module creates Cloudwatch Alarm for Cloudwatch Synthetic canaries on AWS for monitoriing Websites. + +resource "aws_cloudwatch_metric_alarm" "canary_alarm" { + for_each = var.endpoints + + alarm_name = "${each.key}-canary-alarm" + comparison_operator = "GreaterThanOrEqualToThreshold" + evaluation_periods = "1" + metric_name = "Failed" + namespace = "CloudWatchSynthetics" + period = "60" # 1 minute + statistic = "Sum" + threshold = "1" + treat_missing_data = "notBreaching" + + dimensions = { + CanaryName = aws_synthetics_canary.canary[each.key].name + } + + alarm_description = "Canary alarm for ${each.key}" + + alarm_actions = [ + aws_sns_topic.canary_alarm.arn + ] +} + +resource "aws_sns_topic" "canary_alarm" { + name = "dev-xcheck-api-canary-alarm" +} + +resource "aws_sns_topic_subscription" "canary_alarm" { + topic_arn = aws_sns_topic.canary_alarm.arn + protocol = "email" + endpoint = var.alarm_email +} diff --git a/variables.tf b/variables.tf index e69de29..7b8e36e 100644 --- a/variables.tf +++ b/variables.tf @@ -0,0 +1,73 @@ +#Module : LABEL +#Description : Terraform label module variables. +variable "name" { + type = string + default = "" + description = "Name (e.g. `app` or `cluster`)." +} + +variable "repository" { + type = string + default = "https://github.com/clouddrove/terraform-aws-cloudwatch-alarms" + description = "Terraform current module repo" + + validation { + # regex(...) fails if it cannot find a match + condition = can(regex("^https://", var.repository)) + error_message = "The module-repo value must be a valid Git repo link." + } +} + +variable "environment" { + type = string + default = "" + description = "Environment (e.g. `prod`, `dev`, `staging`)." +} + +variable "label_order" { + type = list(any) + default = [] + description = "Label order, e.g. `name`,`application`." +} + +variable "managedby" { + type = string + default = "hello@clouddrove.com" + description = "ManagedBy, eg 'CloudDrove'." +} + +#Module : Synthetic canaries +#Description : Terraform Synthetic canaries module variables. + +variable "s3_artifact_bucket" { + type = string + description = "Location in Amazon S3 where Synthetics stores artifacts from the test runs of this canary" +} + +variable "schedule_expression" { + type = string + description = "Expression defining how often the canary runs" +} + +variable "endpoints" { + type = map(object({ + url = string + })) +} + +variable "alarm_email" { + type = string + description = "Email address to send alarms to" +} + +variable "subnet_ids" { + default = null + type = list(string) + description = "IDs of the subnets where this canary is to run" +} + +variable "security_group_ids" { + default = null + type = list(string) + description = "IDs of the security groups for this canary" +} \ No newline at end of file