From 223f30c8a9e319b361816959711e10301fee9250 Mon Sep 17 00:00:00 2001 From: Erik Osterman Date: Mon, 14 Jan 2019 02:29:11 -0800 Subject: [PATCH] Add webhook support (#14) * Add webhook support * fix outputs * fix args * Ignore special characters * change to form * add missing secret * format * use json * Update docs * Update desc --- README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++--- README.yaml | 39 ++++++++++++++++++++++++++++++++++-- docs/terraform.md | 10 +++++++++- main.tf | 47 ++++++++++++++++++++++++++++++++++++++++++-- outputs.tf | 11 +++++++++++ variables.tf | 32 +++++++++++++++++++++++++++++- 6 files changed, 180 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 3f1b44b..7ab516a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ # terraform-aws-ecs-codepipeline [![Build Status](https://travis-ci.org/cloudposse/terraform-aws-ecs-codepipeline.svg?branch=master)](https://travis-ci.org/cloudposse/terraform-aws-ecs-codepipeline) [![Latest Release](https://img.shields.io/github/release/cloudposse/terraform-aws-ecs-codepipeline.svg)](https://github.com/cloudposse/terraform-aws-ecs-codepipeline/releases/latest) [![Slack Community](https://slack.cloudposse.com/badge.svg)](https://slack.cloudposse.com) -Terraform Module for CI/CD with AWS Code Pipeline and Code Build for ECS. +Terraform Module for CI/CD with AWS Code Pipeline using GitHub webhook triggers and Code Build for ECS. --- @@ -42,8 +42,32 @@ We literally have [*hundreds of terraform modules*][terraform_modules] that are ## Usage + +### Trigger on GitHub Push + +In this example, we'll trigger the pipeline anytime the `master` branch is updated. +```hcl +module "ecs_push_pipeline" { + source = "git::https://github.com/cloudposse/terraform-aws-ecs-codepipeline.git?ref=tags/0.1.2" + name = "app" + namespace = "eg" + stage = "staging" + github_oauth_token = "xxxxxxxxxxxxxx" + repo_owner = "cloudposse" + repo_name = "example" + branch = "master" + service_name = "example" + ecs_cluster_name = "example-ecs-cluster" + privileged_mode = "true" +} +``` + +### Trigger on GitHub Releases + +In this example, we'll trigger anytime a new GitHub release is cut by setting the even type to `release` and using the `json_path` to *exactly* match an `action` of `published`. + ```hcl -module "ecs_codepipeline" { +module "ecs_release_pipeline" { source = "git::https://github.com/cloudposse/terraform-aws-ecs-codepipeline.git?ref=tags/0.1.2" name = "app" namespace = "eg" @@ -55,8 +79,12 @@ module "ecs_codepipeline" { service_name = "example" ecs_cluster_name = "example-ecs-cluster" privileged_mode = "true" + github_webhook_events = ["release"] + webhook_filter_json_path = "$.action" + webhook_filter_match_equals = "published" } ``` +(Thanks to [Stack Overflow](https://stackoverflow.com/questions/52516087/trigger-aws-codepipeline-by-github-release-webhook#comment91997146_52524711)) @@ -94,23 +122,31 @@ Available targets: | enabled | Enable `CodePipeline` creation | string | `true` | no | | environment_variables | A list of maps, that contain both the key 'name' and the key 'value' to be used as additional environment variables for the build. | list | `` | no | | github_oauth_token | GitHub Oauth Token with permissions to access private repositories | string | - | yes | +| github_webhook_events | A list of events which should trigger the webhook. See a list of [available events](https://developer.github.com/v3/activity/events/types/). | list | `` | no | | image_repo_name | ECR repository name to store the Docker image built by this module. Used as CodeBuild ENV variable when building Docker images. [For more info](http://docs.aws.amazon.com/codebuild/latest/userguide/sample-docker.html) | string | `UNSET` | no | | image_tag | Docker image tag in the ECR repository, e.g. 'latest'. Used as CodeBuild ENV variable when building Docker images. [For more info](http://docs.aws.amazon.com/codebuild/latest/userguide/sample-docker.html) | string | `latest` | no | | name | Solution name, e.g. 'app' or 'jenkins' | string | `app` | no | | namespace | Namespace, which could be your organization name, e.g. 'cp' or 'cloudposse' | string | `global` | no | -| poll_source_changes | Periodically check the location of your source content and run the pipeline if changes are detected | string | `true` | no | +| poll_source_changes | Periodically check the location of your source content and run the pipeline if changes are detected | string | `false` | no | | privileged_mode | If set to true, enables running the Docker daemon inside a Docker container on the CodeBuild instance. Used when building Docker images | string | `false` | no | | repo_name | GitHub repository name of the application to be built and deployed to ECS. | string | - | yes | | repo_owner | GitHub Organization or Username. | string | - | yes | | service_name | ECS Service Name | string | - | yes | | stage | Stage, e.g. 'prod', 'staging', 'dev', or 'test' | string | `default` | no | | tags | Additional tags (e.g. `map('BusinessUnit', 'XYZ')` | map | `` | no | +| webhook_authentication | The type of authentication to use. One of IP, GITHUB_HMAC, or UNAUTHENTICATED. | string | `GITHUB_HMAC` | no | +| webhook_enabled | Set to false to prevent the module from creating any webhook resources | string | `true` | no | +| webhook_filter_json_path | The JSON path to filter on. | string | `$.ref` | no | +| webhook_filter_match_equals | The value to match on (e.g. refs/heads/{Branch}) | string | `refs/heads/{Branch}` | no | +| webhook_target_action | The name of the action in a pipeline you want to connect to the webhook. The action must be from the source (first) stage of the pipeline. | string | `Source` | no | ## Outputs | Name | Description | |------|-------------| | badge_url | The URL of the build badge when badge_enabled is enabled | +| webhook_id | The CodePipeline webhook's ARN. | +| webhook_url | The CodePipeline webhook's URL. POST events to this endpoint to trigger the target. | @@ -136,6 +172,14 @@ Check out these related projects. + +## References + +For additional context, refer to some of these links. + +- [aws_codepipeline_webhook](https://www.terraform.io/docs/providers/aws/r/codepipeline_webhook.html) - Provides a CodePipeline Webhook + + ## Help **Got a question?** diff --git a/README.yaml b/README.yaml index c9c42f5..b48cb1c 100644 --- a/README.yaml +++ b/README.yaml @@ -53,12 +53,36 @@ related: # Short description of this project description: |- - Terraform Module for CI/CD with AWS Code Pipeline and Code Build for ECS. + Terraform Module for CI/CD with AWS Code Pipeline using GitHub webhook triggers and Code Build for ECS. # How to use this project usage: |- + + ### Trigger on GitHub Push + + In this example, we'll trigger the pipeline anytime the `master` branch is updated. + ```hcl + module "ecs_push_pipeline" { + source = "git::https://github.com/cloudposse/terraform-aws-ecs-codepipeline.git?ref=tags/0.1.2" + name = "app" + namespace = "eg" + stage = "staging" + github_oauth_token = "xxxxxxxxxxxxxx" + repo_owner = "cloudposse" + repo_name = "example" + branch = "master" + service_name = "example" + ecs_cluster_name = "example-ecs-cluster" + privileged_mode = "true" + } + ``` + + ### Trigger on GitHub Releases + + In this example, we'll trigger anytime a new GitHub release is cut by setting the even type to `release` and using the `json_path` to *exactly* match an `action` of `published`. + ```hcl - module "ecs_codepipeline" { + module "ecs_release_pipeline" { source = "git::https://github.com/cloudposse/terraform-aws-ecs-codepipeline.git?ref=tags/0.1.2" name = "app" namespace = "eg" @@ -70,8 +94,14 @@ usage: |- service_name = "example" ecs_cluster_name = "example-ecs-cluster" privileged_mode = "true" + github_webhook_events = ["release"] + webhook_filter_json_path = "$.action" + webhook_filter_match_equals = "published" } ``` + (Thanks to [Stack Overflow](https://stackoverflow.com/questions/52516087/trigger-aws-codepipeline-by-github-release-webhook#comment91997146_52524711)) + + # Example usage examples: |- @@ -86,6 +116,11 @@ include: - "docs/targets.md" - "docs/terraform.md" +references: + - name: "aws_codepipeline_webhook" + description: "Provides a CodePipeline Webhook" + url: "https://www.terraform.io/docs/providers/aws/r/codepipeline_webhook.html" + # Contributors to this project contributors: - name: "Erik Osterman" diff --git a/docs/terraform.md b/docs/terraform.md index e08c343..db1c96f 100644 --- a/docs/terraform.md +++ b/docs/terraform.md @@ -16,21 +16,29 @@ | enabled | Enable `CodePipeline` creation | string | `true` | no | | environment_variables | A list of maps, that contain both the key 'name' and the key 'value' to be used as additional environment variables for the build. | list | `` | no | | github_oauth_token | GitHub Oauth Token with permissions to access private repositories | string | - | yes | +| github_webhook_events | A list of events which should trigger the webhook. See a list of [available events](https://developer.github.com/v3/activity/events/types/). | list | `` | no | | image_repo_name | ECR repository name to store the Docker image built by this module. Used as CodeBuild ENV variable when building Docker images. [For more info](http://docs.aws.amazon.com/codebuild/latest/userguide/sample-docker.html) | string | `UNSET` | no | | image_tag | Docker image tag in the ECR repository, e.g. 'latest'. Used as CodeBuild ENV variable when building Docker images. [For more info](http://docs.aws.amazon.com/codebuild/latest/userguide/sample-docker.html) | string | `latest` | no | | name | Solution name, e.g. 'app' or 'jenkins' | string | `app` | no | | namespace | Namespace, which could be your organization name, e.g. 'cp' or 'cloudposse' | string | `global` | no | -| poll_source_changes | Periodically check the location of your source content and run the pipeline if changes are detected | string | `true` | no | +| poll_source_changes | Periodically check the location of your source content and run the pipeline if changes are detected | string | `false` | no | | privileged_mode | If set to true, enables running the Docker daemon inside a Docker container on the CodeBuild instance. Used when building Docker images | string | `false` | no | | repo_name | GitHub repository name of the application to be built and deployed to ECS. | string | - | yes | | repo_owner | GitHub Organization or Username. | string | - | yes | | service_name | ECS Service Name | string | - | yes | | stage | Stage, e.g. 'prod', 'staging', 'dev', or 'test' | string | `default` | no | | tags | Additional tags (e.g. `map('BusinessUnit', 'XYZ')` | map | `` | no | +| webhook_authentication | The type of authentication to use. One of IP, GITHUB_HMAC, or UNAUTHENTICATED. | string | `GITHUB_HMAC` | no | +| webhook_enabled | Set to false to prevent the module from creating any webhook resources | string | `true` | no | +| webhook_filter_json_path | The JSON path to filter on. | string | `$.ref` | no | +| webhook_filter_match_equals | The value to match on (e.g. refs/heads/{Branch}) | string | `refs/heads/{Branch}` | no | +| webhook_target_action | The name of the action in a pipeline you want to connect to the webhook. The action must be from the source (first) stage of the pipeline. | string | `Source` | no | ## Outputs | Name | Description | |------|-------------| | badge_url | The URL of the build badge when badge_enabled is enabled | +| webhook_id | The CodePipeline webhook's ARN. | +| webhook_url | The CodePipeline webhook's URL. POST events to this endpoint to trigger the target. | diff --git a/main.tf b/main.tf index 56b7427..b6bd9d9 100644 --- a/main.tf +++ b/main.tf @@ -162,6 +162,10 @@ data "aws_iam_policy_document" "codebuild" { } } +data "aws_caller_identity" "default" {} + +data "aws_region" "default" {} + module "build" { source = "git::https://github.com/cloudposse/terraform-aws-codebuild.git?ref=tags/0.11.0" enabled = "${var.enabled}" @@ -260,6 +264,45 @@ resource "aws_codepipeline" "source_build_deploy" { } } -data "aws_caller_identity" "default" {} +resource "random_string" "webhook_secret" { + count = "${var.webhook_enabled == "true" ? 1 : 0}" + length = 32 -data "aws_region" "default" {} + # Special characters are not allowed in webhook secret (AWS silently ignores webhook callbacks) + special = false +} + +locals { + webhook_secret = "${join("", random_string.webhook_secret.*.result)}" + webhook_url = "${join("", aws_codepipeline_webhook.webhook.*.url)}" +} + +resource "aws_codepipeline_webhook" "webhook" { + count = "${var.webhook_enabled == "true" ? 1 : 0}" + name = "${module.codepipeline_label.id}" + authentication = "${var.webhook_authentication}" + target_action = "${var.webhook_target_action}" + target_pipeline = "${aws_codepipeline.source_build_deploy.name}" + + authentication_configuration { + secret_token = "${local.webhook_secret}" + } + + filter { + json_path = "${var.webhook_filter_json_path}" + match_equals = "${var.webhook_filter_match_equals}" + } +} + +module "github_webhooks" { + source = "git::https://github.com/cloudposse/terraform-github-repository-webhooks.git?ref=tags/0.1.1" + enabled = "${var.webhook_enabled}" + github_organization = "${var.repo_owner}" + github_repositories = ["${var.repo_name}"] + github_token = "${var.github_oauth_token}" + webhook_url = "${local.webhook_url}" + webhook_secret = "${local.webhook_secret}" + webhook_content_type = "json" + name = "web" + events = ["${var.github_webhook_events}"] +} diff --git a/outputs.tf b/outputs.tf index 0953212..badab94 100644 --- a/outputs.tf +++ b/outputs.tf @@ -2,3 +2,14 @@ output "badge_url" { description = "The URL of the build badge when badge_enabled is enabled" value = "${module.build.badge_url}" } + +output "webhook_id" { + description = "The CodePipeline webhook's ARN." + value = "${join("", aws_codepipeline_webhook.webhook.*.id)}" +} + +output "webhook_url" { + description = "The CodePipeline webhook's URL. POST events to this endpoint to trigger the target." + value = "${local.webhook_url}" + sensitive = true +} diff --git a/variables.tf b/variables.tf index a41e9a9..7ccd1d6 100644 --- a/variables.tf +++ b/variables.tf @@ -32,6 +32,11 @@ variable "github_oauth_token" { description = "GitHub Oauth Token with permissions to access private repositories" } +variable "github_webhook_events" { + description = "A list of events which should trigger the webhook. See a list of [available events](https://developer.github.com/v3/activity/events/types/)." + default = ["push"] +} + variable "repo_owner" { description = "GitHub Organization or Username." } @@ -75,7 +80,7 @@ variable "buildspec" { # It is recommended you avoid using boolean values and use explicit strings variable "poll_source_changes" { type = "string" - default = "true" + default = "false" description = "Periodically check the location of your source content and run the pipeline if changes are detected" } @@ -136,3 +141,28 @@ variable "environment_variables" { description = "A list of maps, that contain both the key 'name' and the key 'value' to be used as additional environment variables for the build." } + +variable "webhook_enabled" { + description = "Set to false to prevent the module from creating any webhook resources" + default = "true" +} + +variable "webhook_target_action" { + description = "The name of the action in a pipeline you want to connect to the webhook. The action must be from the source (first) stage of the pipeline." + default = "Source" +} + +variable "webhook_authentication" { + description = "The type of authentication to use. One of IP, GITHUB_HMAC, or UNAUTHENTICATED." + default = "GITHUB_HMAC" +} + +variable "webhook_filter_json_path" { + description = "The JSON path to filter on." + default = "$.ref" +} + +variable "webhook_filter_match_equals" { + description = "The value to match on (e.g. refs/heads/{Branch})" + default = "refs/heads/{Branch}" +}