├── .bumpversion.cfg ├── .editorconfig ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── lint.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── .mergify.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── main.tf ├── modules ├── _internal │ ├── README.md │ ├── handler │ │ ├── lambda.py │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── runner │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── trigger │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf ├── branch │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── on-demand │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── review │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── schedule │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf └── tag │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── outputs.tf ├── tests ├── branch │ └── main.tf ├── main │ └── main.tf ├── main_vpc │ └── main.tf ├── review │ └── main.tf ├── schedule │ └── main.tf └── tag │ └── main.tf ├── variables.tf └── versions.tf /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 5.3.1 3 | commit = True 4 | message = Bumps version to {new_version} 5 | tag = False 6 | tag_name = {new_version} 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | end_of_line = lf 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 2 10 | charset = utf-8 11 | tab_width = 4 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | 16 | [*.py] 17 | indent_size = 4 18 | 19 | [Makefile] 20 | indent_style = tab 21 | indent_size = 1 22 | 23 | [LICENSE] 24 | indent_size = none 25 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text eol=lf 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for GitHub Actions 4 | - package-ecosystem: github-actions 5 | directory: / 6 | schedule: 7 | interval: weekly 8 | - package-ecosystem: docker 9 | directory: "/" 10 | schedule: 11 | interval: daily 12 | open-pull-requests-limit: 10 13 | - package-ecosystem: terraform 14 | directory: "/modules/_internal/handler" 15 | schedule: 16 | interval: daily 17 | open-pull-requests-limit: 10 18 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Run lint and static analyis checks 2 | on: 3 | pull_request: 4 | 5 | concurrency: 6 | group: lint-${{ github.head_ref || github.ref }} 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | lint: 11 | uses: plus3it/actions-workflows/.github/workflows/lint.yml@78caa4f6a2b5426af0ade68fb706176ee58fda84 12 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create GitHub Release 2 | 3 | on: 4 | # Run on demand 5 | workflow_dispatch: 6 | 7 | # Run on push to main when .bumpversion.cfg version is updated 8 | push: 9 | branches: 10 | - main 11 | - master 12 | paths: 13 | - .bumpversion.cfg 14 | 15 | jobs: 16 | release: 17 | uses: plus3it/actions-workflows/.github/workflows/release.yml@78caa4f6a2b5426af0ade68fb706176ee58fda84 18 | with: 19 | mockstacktest-enable: false 20 | secrets: 21 | release-token: ${{ secrets.GH_RELEASES_TOKEN }} 22 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run test jobs 2 | on: 3 | pull_request: 4 | 5 | concurrency: 6 | group: test-${{ github.head_ref || github.ref }} 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | test: 11 | uses: plus3it/actions-workflows/.github/workflows/test.yml@78caa4f6a2b5426af0ade68fb706176ee58fda84 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Terraform-managed files 2 | *.tfstate 3 | *.tfstate.backup 4 | .terraform 5 | terraform.tfstate.d 6 | 7 | # Python local files 8 | __pycache__ 9 | 10 | # tardigrade-ci 11 | .tardigrade-ci 12 | tardigrade-ci/ 13 | 14 | # terraform lock file 15 | .terraform.lock.hcl 16 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | - name: approve dependabot pull requests 3 | conditions: 4 | - author=dependabot[bot] 5 | actions: 6 | review: 7 | type: APPROVE 8 | 9 | - name: merge dependabot pull requests 10 | conditions: 11 | - author=dependabot[bot] 12 | - "#approved-reviews-by>=1" 13 | actions: 14 | merge: 15 | method: merge 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM plus3it/tardigrade-ci:0.27.0 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 Maintainers of plus3it/terraform-aws-codecommit-flow-ci 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | export ONLY_MOTO := true 4 | 5 | include $(shell test -f .tardigrade-ci || curl -sSL -o .tardigrade-ci "https://raw.githubusercontent.com/plus3it/tardigrade-ci/master/bootstrap/Makefile.bootstrap"; echo .tardigrade-ci) 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![pullreminders](https://pullreminders.com/badge.svg)](https://pullreminders.com?ref=badge) 2 | 3 | # terraform-aws-codecommit-flow-ci 4 | 5 | Implement an event-based CI workflow on a CodeCommit repository. 6 | 7 | This project aims to help implement CI/CD git workflows for CodeCommit 8 | repositories. Fundamentally, we want to be able to trigger the CI system 9 | (CodeBuild) when certain events occur in the CodeCommit reopository: 10 | 11 | * Pull request opened or source commit modified 12 | * Branch HEAD modified 13 | * Tag created or updated 14 | * Scheduled build (e.g. "cron") 15 | 16 | All of the building blocks are there, pull requests, CloudWatch Events, etc, 17 | but understanding the event structures and linking the events to the CI system 18 | is a lot of work. This project makes it easier. 19 | 20 | For each event, the user ought to be able to specify what the CI system should 21 | do, what commands should be executed. In CodeBuild, this is accomplished by 22 | using a different buildspec per event. To simplify the implementation of this 23 | project, at the moment, a CodeBuild project is created for each event, and of 24 | course for each CodeBuild project you can specify a different buildspec. 25 | 26 | ## Public modules 27 | 28 | The top-level module is a wrapper around each of the "event" modules. There is 29 | also a public module for each of the events mentioned above: 30 | 31 | * [branch](modules/branch) 32 | * [on-demand](modules/on-demand) 33 | * [review](modules/review) -- i.e. the pull request event 34 | * [tag](modules/tag) 35 | * [schedule](modules/schedule) 36 | 37 | In general, each module sets up the following resources: 38 | 39 | * CloudWatch Events 40 | * Lambda 41 | * CodeBuild 42 | 43 | When a matching event occurs in the repository, CloudWatch Events triggers the 44 | Lambda function. The Lambda function extracts information from the event, most 45 | critically the source commit, and starts the CodeBuild job. 46 | 47 | The `review` module additionally uses CloudWatch Events to monitor the status 48 | of its CodeBuild job executions and comments on the associated pull request 49 | with the status of the CodeBuild job. CodeCommit does not have anything like 50 | the GitHub Status API or Checks API, so these comments at least allow users to 51 | get updates on whether the CI passed/failed right within the pull request. 52 | 53 | The `on-demand` module is different in that it has no Lambda, and no CloudWatch 54 | Event to trigger it. Instead, it is expected that the user will trigger the 55 | job "on demand" using the StartBuild API, or otherwise integrate it into their 56 | own workflow. 57 | 58 | ## A complete example workflow 59 | 60 | In this example, we setup the CI to execute automatically on four events: 61 | 62 | * A pull request is opened or updated (`review` module) 63 | * The `master` branch is updated (`branch` module) 64 | * A tag is created or updated (`tag` module) 65 | * A weekday schedule (`schedule` module) 66 | 67 | We have separate buildspecs for each event-type, and we keep those buildspecs 68 | together in the repository, in the `buildspecs` directory. 69 | 70 | In this workflow, someone would open a pull request and the CI would trigger 71 | immediately to execute tests (as defined by `buildspecs/review.yaml`). The CI 72 | would post success/failure of the tests as a pull request comment, and an 73 | approver would make the decision when to merge the work. 74 | 75 | Upon merge to the `master` branch, the `branch` CI executes whatever is defined 76 | in `buidspecs/master.yaml`. Imagine there is a test for a condition that we use 77 | to determine when to create a release (such as incrementing a version in a 78 | version file). When that condition is matched, the `branch` buildspec pushes a 79 | tag to the repo with the new version. To grant permission for this CodeBuild 80 | job to push tags to the repo, we pass in the `policy_override` (defined in the `locals` block, in this example). 81 | 82 | When the tag is created, the `tag` CI then executes the job as defined by 83 | `buildspecs/tag.yaml` to handle the release. Examples of things a buildspec 84 | might do in this case: 85 | 86 | * Publish a package to a repository (PyPI, RubyGems, npm, etc) 87 | * Generate and push artifacts to S3 88 | * Initiate a CodePipeline 89 | * Launch/update a CloudFormation stack 90 | * Run terraform plan/apply 91 | * Etc, etc, whatever constitutes your "release"... 92 | 93 | ```hcl 94 | module "review" { 95 | source = "git::https://github.com/plus3it/terraform-aws-codecommit-flow-ci.git" 96 | 97 | event = "review" 98 | repo_name = "foo" 99 | buildspec = "buildspecs/review.yaml" 100 | } 101 | 102 | module "branch" { 103 | source = "git::https://github.com/plus3it/terraform-aws-codecommit-flow-ci.git" 104 | 105 | event = "branch" 106 | repo_name = "foo" 107 | branch = "master" 108 | buildspec = "buildspecs/master.yaml" 109 | 110 | policy_override = local.branch_policy_override 111 | } 112 | 113 | module "tag" { 114 | source = "git::https://github.com/plus3it/terraform-aws-codecommit-flow-ci.git" 115 | 116 | event = "tag" 117 | repo_name = "foo" 118 | buildspec = "buildspecs/tag.yaml" 119 | } 120 | 121 | module "schedule" { 122 | source = "git::https://github.com/plus3it/terraform-aws-codecommit-flow-ci.git" 123 | 124 | event = "schedule" 125 | repo_name = "foo" 126 | buildspec = "buildspecs/schedule.yaml" 127 | 128 | schedule_expression = "cron(0 11 ? * MON-FRI *)" 129 | } 130 | 131 | locals { 132 | branch_policy_override = <<-OVERRIDE 133 | { 134 | "Version": "2012-10-17", 135 | "Statement": [ 136 | { 137 | "Action": "codecommit:GitPush", 138 | "Condition": { 139 | "StringLikeIfExists": { 140 | "codecommit:References": [ 141 | "refs/tags/*" 142 | ] 143 | } 144 | }, 145 | "Effect": "Allow", 146 | "Resource": "arn::codecommit:::foo", 147 | "Sid": "" 148 | } 149 | ] 150 | } 151 | OVERRIDE 152 | } 153 | ``` 154 | 155 | ### `buildspec` variable object 156 | 157 | The `buildspec` variable object is a string that can be either a relative path 158 | in the repository to the buildspec file (e.g. `buildspec.yaml`), or a complete 159 | multi-line string buildspec specification. 160 | 161 | The default is the file `buildspec.yaml`, which would need to be present in 162 | the root of your CodeCommit repository. If the file is missing, the job will 163 | simply error. 164 | 165 | To use a multi-line string as a buildspec, see the example below. This 166 | specification contains no commands and so actually does nothing: 167 | 168 | ```hcl 169 | buildspec = <<-BUILDSPEC 170 | version: 0.2 171 | phases: {} 172 | BUILDSPEC 173 | ``` 174 | 175 | See the [AWS CodeBuild docs][codebuild-buildspec] for a complete description 176 | of the buildspec specification. 177 | 178 | [codebuild-buildspec]: https://docs.aws.amazon.com/codebuild/latest/userguide/build-spec-ref.html#build-spec-ref-syntax 179 | 180 | ### `artifacts` variable object 181 | 182 | The `artifacts` variable is a map that is passed through to the `artifacts` 183 | option of the Terraform `aws_codebuild_project` resource. It defaults to: 184 | 185 | ```hcl 186 | artifacts = { 187 | type = "NO_ARTIFACTS" 188 | } 189 | ``` 190 | 191 | See the [Terraform resource docs][terraform-codebuild-artifacts] for all the 192 | available options. 193 | 194 | [terraform-codebuild-artifacts]: https://www.terraform.io/docs/providers/aws/r/codebuild_project.html#artifacts 195 | 196 | ### `environment` variable object 197 | 198 | The `environment` variable is a map that is passed through to the `environment` 199 | option of the Terraform `aws_codebuild_project` resource. It defaults to: 200 | 201 | ```hcl 202 | environment = { 203 | compute_type = "BUILD_GENERAL1_SMALL" 204 | image = "aws/codebuild/nodejs:8.11.0" 205 | type = "LINUX_CONTAINER" 206 | } 207 | ``` 208 | 209 | See the [Terraform resource docs][terraform-codebuild-environment] for all the 210 | available options. 211 | 212 | [terraform-codebuild-environment]: https://www.terraform.io/docs/providers/aws/r/codebuild_project.html#environment 213 | 214 | ### `environment_variables` variable object 215 | 216 | The `environment_variables` variable is a list of environment variable map 217 | objects that is merged into to the `environment` object (described above). It 218 | defaults to an empty list, meaning no environment variables. Example: 219 | 220 | ```hcl 221 | environment_variables = [ 222 | { 223 | name = "FOO" 224 | value = "foo" 225 | }, 226 | { 227 | name = "BAR" 228 | value = "bar" 229 | } 230 | ] 231 | ``` 232 | 233 | See the [Terraform resource docs][terraform-codebuild-environment] for a more 234 | thorough description of the options for the environment variable map object. 235 | 236 | ### `policy_arns` variable object 237 | 238 | The `policy_arns` variable is a list of IAM policy ARNs to attach to the 239 | CodeBuild service role, or null to support ignoring externally attached policies 240 | Example: 241 | 242 | ```hcl 243 | policy_arns = [ 244 | "arn::iam:::policy/foo", 245 | "arn::iam:::policy/bar" 246 | ] 247 | ``` 248 | 249 | ```hcl 250 | policy_arns = null 251 | ``` 252 | 253 | ### `policy_override` variable object 254 | 255 | The `policy_override` variable is an IAM policy document in JSON that extends 256 | the builtin CodeBuild service role. This option is provided as an alternative 257 | to creating an IAM managed policy and passing the policy through `policy_arns`. 258 | It is a convenient way to grant a small number of additional permissions to a 259 | single CI job. Example: 260 | 261 | ```hcl 262 | policy_override = <<-OVERRIDE 263 | { 264 | "Version": "2012-10-17", 265 | "Statement": [ 266 | { 267 | "Action": "codecommit:GitPush", 268 | "Condition": { 269 | "StringLikeIfExists": { 270 | "codecommit:References": [ 271 | "refs/tags/*" 272 | ] 273 | } 274 | }, 275 | "Effect": "Allow", 276 | "Resource": "arn::codecommit:::foo", 277 | "Sid": "" 278 | } 279 | ] 280 | } 281 | OVERRIDE 282 | ``` 283 | 284 | ## CodeBuild environment variable injection 285 | 286 | The Lambda function for each event injects one or more environment variables 287 | into the corresponding CodeBuild job, using information from the event that 288 | invoked the function. These variables are available in the job environment, and 289 | so you may reference them from your buildspecs. 290 | 291 | * `review` 292 | * `FLOW_PULL_REQUEST_ID`: ID of the pull request that triggered the event 293 | * `FLOW_PULL_REQUEST_SRC_COMMIT`: SHA of the source commit in the pull 294 | request 295 | * `FLOW_PULL_REQUEST_DST_COMMIT`: SHA of the destination commit (the 296 | target branch) in the pull request 297 | * `branch` 298 | * `FLOW_BRANCH`: Name of the branch that triggered the event 299 | * `tag` 300 | * `FLOW_TAG`: Name of the tag that triggered the event 301 | * `schedule` 302 | * `FLOW_SCHEDULE`: Time associated with the scheduled event 303 | 304 | ## Builtin CodeBuild service role 305 | 306 | A default service role will be created for each CodeBuild job. The service role 307 | has just enough permissions to create and write to the job's CloudWatch Log 308 | Group, and to clone the CodeCommit repository. This service role can be 309 | extended using the `policy_arns` or `policy_override` variables. 310 | 311 | ```hcl 312 | statement { 313 | actions = [ 314 | "logs:CreateLogGroup", 315 | "logs:CreateLogStream", 316 | "logs:PutLogEvents", 317 | ] 318 | 319 | resources = [ 320 | "arn::logs:::log-group:/aws/codebuild/${local.name_slug}", 321 | "arn::logs:::log-group:/aws/codebuild/${local.name_slug}:*", 322 | ] 323 | } 324 | 325 | statement { 326 | actions = ["codecommit:GitPull"] 327 | resources = ["arn::codecommit:::${var.repo_name}"] 328 | } 329 | ``` 330 | 331 | ## Builtin Lambda service role 332 | 333 | A default service role will be created for each Lambda function. The service 334 | role has just enough permissions to create and write to the function's 335 | CloudWatch Log Group, and to start the CodeBuild job. There are no user 336 | variables exposed that extend or modify this role. 337 | 338 | ```hcl 339 | statement { 340 | actions = [ 341 | "logs:CreateLogGroup", 342 | "logs:CreateLogStream", 343 | "logs:PutLogEvents", 344 | ] 345 | 346 | resources = [ 347 | "arn::logs:::log-group:/aws/lambda/${local.name_slug}", 348 | "arn::logs:::log-group:/aws/lambda/${local.name_slug}:*", 349 | ] 350 | } 351 | 352 | statement { 353 | actions = ["codebuild:StartBuild"] 354 | resources = ["arn::codebuild:::project/${local.name_slug}"] 355 | } 356 | ``` 357 | 358 | In addition, the `review` module extends the Lambda service role with 359 | permissions that allow the function to post comments to the pull request, and 360 | to retrieve logs from the CodeBuild job (for inclusion in the pull request 361 | comment). 362 | 363 | ```hcl 364 | statement { 365 | actions = ["codecommit:PostCommentForPullRequest"] 366 | resources = ["arn::codecommit:::${var.repo_name}"] 367 | } 368 | 369 | statement { 370 | actions = ["logs:GetLogEvents"] 371 | resources = ["arn::logs:::log-group:/aws/codebuild/${var.repo_name}-review-flow-ci:log-stream:*"] 372 | } 373 | ``` 374 | 375 | ## Testing 376 | 377 | At the moment, testing is manual: 378 | 379 | ``` 380 | # Replace "xxx" with an actual AWS profile, then execute the integration tests. 381 | export AWS_PROFILE=xxx 382 | make terraform/pytest PYTEST_ARGS="-v --nomock" 383 | ``` 384 | 385 | ## Authors 386 | 387 | This module is managed by [Plus3 IT Systems](https://github.com/plus3it). 388 | 389 | ## License 390 | 391 | Apache 2 licensed. See [LICENSE](LICENSE) for details. 392 | 393 | 394 | ## Requirements 395 | 396 | | Name | Version | 397 | |------|---------| 398 | | [terraform](#requirement\_terraform) | >= 0.13 | 399 | | [aws](#requirement\_aws) | >= 3.28.0 | 400 | 401 | ## Providers 402 | 403 | No providers. 404 | 405 | ## Resources 406 | 407 | No resources. 408 | 409 | ## Inputs 410 | 411 | | Name | Description | Type | Default | Required | 412 | |------|-------------|------|---------|:--------:| 413 | | [event](#input\_event) | Type of event that will trigger the flow-ci job | `string` | n/a | yes | 414 | | [repo\_name](#input\_repo\_name) | Name of the CodeCommit repository | `string` | n/a | yes | 415 | | [artifacts](#input\_artifacts) | Map defining an artifacts object for the CodeBuild job | `map(string)` | `{}` | no | 416 | | [badge\_enabled](#input\_badge\_enabled) | Generates a publicly-accessible URL for the projects build badge | `bool` | `null` | no | 417 | | [branch](#input\_branch) | Name of the branch where updates will trigger a build. Used only when `event` is "branch" | `string` | `null` | no | 418 | | [build\_timeout](#input\_build\_timeout) | How long in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait until timing out any related build that does not get marked as completed | `number` | `null` | no | 419 | | [buildspec](#input\_buildspec) | Buildspec used when the specified branch is updated | `string` | `""` | no | 420 | | [encryption\_key](#input\_encryption\_key) | The AWS Key Management Service (AWS KMS) customer master key (CMK) to be used for encrypting the build project's build output artifacts | `string` | `null` | no | 421 | | [environment](#input\_environment) | Map describing the environment object for the CodeBuild job | `map(string)` | `{}` | no | 422 | | [environment\_variables](#input\_environment\_variables) | List of environment variable map objects for the CodeBuild job | `list(map(string))` | `[]` | no | 423 | | [name\_prefix](#input\_name\_prefix) | Prefix to attach to repo name | `string` | `""` | no | 424 | | [policy\_arns](#input\_policy\_arns) | List of IAM policy ARNs to attach to the CodeBuild service role | `list(string)` | `[]` | no | 425 | | [policy\_override](#input\_policy\_override) | IAM policy document in JSON that extends the basic inline CodeBuild service role | `string` | `""` | no | 426 | | [python\_runtime](#input\_python\_runtime) | Python runtime for the handler Lambda function | `string` | `null` | no | 427 | | [queued\_timeout](#input\_queued\_timeout) | How long in minutes, from 5 to 480 (8 hours), a build is allowed to be queued before it times out | `number` | `null` | no | 428 | | [schedule\_expression](#input\_schedule\_expression) | CloudWatch Event schedule that triggers the CodeBuild job. Required when `event` is "schedule" | `string` | `null` | no | 429 | | [source\_version](#input\_source\_version) | A version of the build input to be built for this project. If not specified, the latest version is used | `string` | `null` | no | 430 | | [tags](#input\_tags) | A map of tags to assign to the resource | `map(string)` | `{}` | no | 431 | | [vpc\_config](#input\_vpc\_config) | Object of inputs for the VPC configuration of the CodeBuild job |
object({
security_group_ids = list(string)
subnets = list(string)
vpc_id = string
})
| `null` | no | 432 | 433 | ## Outputs 434 | 435 | | Name | Description | 436 | |------|-------------| 437 | | [branch](#output\_branch) | Outputs from the branch module | 438 | | [review](#output\_review) | Outputs from the review module | 439 | | [schedule](#output\_schedule) | Outputs from the schedule module | 440 | | [tag](#output\_tag) | Outputs from the tag module | 441 | 442 | 443 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | module "branch" { 2 | source = "./modules/branch" 3 | count = var.event == "branch" ? 1 : 0 4 | 5 | branch = var.branch 6 | name_prefix = var.name_prefix 7 | repo_name = var.repo_name 8 | 9 | artifacts = var.artifacts 10 | buildspec = var.buildspec 11 | badge_enabled = var.badge_enabled 12 | build_timeout = var.build_timeout 13 | encryption_key = var.encryption_key 14 | environment = var.environment 15 | environment_variables = var.environment_variables 16 | policy_arns = var.policy_arns 17 | policy_override = var.policy_override 18 | python_runtime = var.python_runtime 19 | queued_timeout = var.queued_timeout 20 | source_version = var.source_version 21 | tags = var.tags 22 | vpc_config = var.vpc_config 23 | } 24 | 25 | module "on_demand" { 26 | source = "./modules/on-demand" 27 | count = var.event == "on-demand" ? 1 : 0 28 | 29 | name_prefix = var.name_prefix 30 | repo_name = var.repo_name 31 | 32 | artifacts = var.artifacts 33 | buildspec = var.buildspec 34 | badge_enabled = var.badge_enabled 35 | build_timeout = var.build_timeout 36 | encryption_key = var.encryption_key 37 | environment = var.environment 38 | environment_variables = var.environment_variables 39 | policy_arns = var.policy_arns 40 | policy_override = var.policy_override 41 | queued_timeout = var.queued_timeout 42 | source_version = var.source_version 43 | tags = var.tags 44 | vpc_config = var.vpc_config 45 | } 46 | 47 | module "review" { 48 | source = "./modules/review" 49 | count = var.event == "review" ? 1 : 0 50 | 51 | name_prefix = var.name_prefix 52 | repo_name = var.repo_name 53 | 54 | artifacts = var.artifacts 55 | buildspec = var.buildspec 56 | badge_enabled = var.badge_enabled 57 | build_timeout = var.build_timeout 58 | encryption_key = var.encryption_key 59 | environment = var.environment 60 | environment_variables = var.environment_variables 61 | policy_arns = var.policy_arns 62 | policy_override = var.policy_override 63 | python_runtime = var.python_runtime 64 | queued_timeout = var.queued_timeout 65 | source_version = var.source_version 66 | tags = var.tags 67 | vpc_config = var.vpc_config 68 | } 69 | 70 | module "schedule" { 71 | source = "./modules/schedule" 72 | count = var.event == "schedule" ? 1 : 0 73 | 74 | name_prefix = var.name_prefix 75 | repo_name = var.repo_name 76 | schedule_expression = var.schedule_expression 77 | 78 | artifacts = var.artifacts 79 | buildspec = var.buildspec 80 | badge_enabled = var.badge_enabled 81 | build_timeout = var.build_timeout 82 | encryption_key = var.encryption_key 83 | environment = var.environment 84 | environment_variables = var.environment_variables 85 | policy_arns = var.policy_arns 86 | policy_override = var.policy_override 87 | python_runtime = var.python_runtime 88 | queued_timeout = var.queued_timeout 89 | source_version = var.source_version 90 | tags = var.tags 91 | vpc_config = var.vpc_config 92 | } 93 | 94 | module "tag" { 95 | source = "./modules/tag" 96 | count = var.event == "tag" ? 1 : 0 97 | 98 | name_prefix = var.name_prefix 99 | repo_name = var.repo_name 100 | 101 | artifacts = var.artifacts 102 | buildspec = var.buildspec 103 | badge_enabled = var.badge_enabled 104 | build_timeout = var.build_timeout 105 | encryption_key = var.encryption_key 106 | environment = var.environment 107 | environment_variables = var.environment_variables 108 | policy_arns = var.policy_arns 109 | policy_override = var.policy_override 110 | python_runtime = var.python_runtime 111 | queued_timeout = var.queued_timeout 112 | source_version = var.source_version 113 | tags = var.tags 114 | vpc_config = var.vpc_config 115 | } 116 | -------------------------------------------------------------------------------- /modules/_internal/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Requirements 3 | 4 | No requirements. 5 | 6 | ## Providers 7 | 8 | No providers. 9 | 10 | ## Resources 11 | 12 | No resources. 13 | 14 | ## Inputs 15 | 16 | No inputs. 17 | 18 | ## Outputs 19 | 20 | No outputs. 21 | 22 | 23 | -------------------------------------------------------------------------------- /modules/_internal/handler/lambda.py: -------------------------------------------------------------------------------- 1 | """Lambda handlers that implement an event-based CI workflow for CodeCommit.""" 2 | 3 | import collections 4 | import json 5 | import logging 6 | import os 7 | import urllib.parse 8 | 9 | import boto3 10 | 11 | DEFAULT_LOG_LEVEL = logging.INFO 12 | LOG_LEVELS = collections.defaultdict( 13 | lambda: DEFAULT_LOG_LEVEL, 14 | { 15 | "critical": logging.CRITICAL, 16 | "error": logging.ERROR, 17 | "warning": logging.WARNING, 18 | "info": logging.INFO, 19 | "debug": logging.DEBUG, 20 | }, 21 | ) 22 | 23 | # Lambda initializes a root logger that needs to be removed in order to set a 24 | # different logging config 25 | root = logging.getLogger() 26 | if root.handlers: 27 | for handler in root.handlers: 28 | root.removeHandler(handler) 29 | 30 | logging.basicConfig( 31 | format="%(asctime)s.%(msecs)03dZ [%(name)s][%(levelname)-5s]: %(message)s", 32 | datefmt="%Y-%m-%dT%H:%M:%S", 33 | level=LOG_LEVELS[os.environ.get("LOG_LEVEL", "").lower()], 34 | ) 35 | log = logging.getLogger(__name__) 36 | 37 | codebuild = boto3.client("codebuild") 38 | codecommit = boto3.client("codecommit") 39 | cloudwatchlogs = boto3.client("logs") 40 | 41 | PROJECT_NAME = os.environ["PROJECT_NAME"] 42 | 43 | 44 | def start_build(params): 45 | """Start a CodeBuild job.""" 46 | log.info("Sending request to StartBuild...") 47 | log.debug("StartBuild params:\n%s", params) 48 | response = codebuild.start_build(**params) 49 | log.info("StartBuild succeeded!") 50 | log.debug("StartBuild response:\n%s", response) 51 | return response 52 | 53 | 54 | def post_comment(params): 55 | """Post a comment to a CodeCommit pull request.""" 56 | log.info("Sending request to PostComment...") 57 | log.debug("PostComment params:\n%s", params) 58 | response = codecommit.post_comment_for_pull_request(**params) 59 | log.info("PostComment succeeded!") 60 | log.debug("PostComment response:\n%s", response) 61 | return response 62 | 63 | 64 | def handle_codebuild_review_event(event): # pylint: disable=too-many-locals 65 | """Gather build details and post a comment to a managed pull request.""" 66 | event_details = event["detail"] 67 | additional_information = event_details["additional-information"] 68 | environment = additional_information["environment"] 69 | environment_variables = environment["environment-variables"] 70 | 71 | pull_request_id = [ 72 | env["value"] 73 | for env in environment_variables 74 | if env["name"] == "FLOW_PULL_REQUEST_ID" 75 | ] 76 | source_commit = [ 77 | env["value"] 78 | for env in environment_variables 79 | if env["name"] == "FLOW_PULL_REQUEST_SRC_COMMIT" 80 | ] 81 | destination_commit = [ 82 | env["value"] 83 | for env in environment_variables 84 | if env["name"] == "FLOW_PULL_REQUEST_DST_COMMIT" 85 | ] 86 | 87 | if pull_request_id and source_commit and destination_commit: 88 | logs = additional_information.get("logs", {}) 89 | log_group = logs.get("group-name") 90 | log_stream = logs.get("stream-name") 91 | log_deep_link = logs.get("deep-link") 92 | 93 | build_arn = event_details["build-id"] 94 | build_id = build_arn.split("/")[-1] 95 | build_uuid = build_id.split(":")[-1] 96 | source_url = additional_information["source"]["location"] 97 | repo_name = source_url.split("/")[-1] 98 | project_name = event_details["project-name"] 99 | build_status = event_details["build-status"] 100 | console_host = urllib.parse.urlparse(log_deep_link).hostname 101 | region = event["region"] 102 | request_token = build_arn + build_status 103 | safe_build_id = urllib.parse.quote(build_id, safe="~@#$&()*!+=:;,.?/'") 104 | job_url = ( 105 | f"https://{region}.{console_host}/codebuild/home?" 106 | f"region={region}#/builds/{safe_build_id}/view/new" 107 | ) 108 | 109 | # Construct the base comment on the build status 110 | build_status_map = { 111 | "IN_PROGRESS": ("inProgress", "is **IN PROGRESS**. "), 112 | "SUCCEEDED": ("passing", "**SUCCEEDED**! "), 113 | "STOPPED": ("failing", "was **CANCELED**. "), 114 | "TIMED_OUT": ("failing", "**TIMED OUT**. "), 115 | } 116 | 117 | comment_details = build_status_map.get( 118 | build_status, ("failing", "**FAILED**. ") 119 | ) 120 | 121 | badge_link = ( 122 | f"![{comment_details[0]}](https://s3.{region}.amazonaws.com/" 123 | f"codefactory-{region}-prod-default-build-badges/" 124 | f'{comment_details[0]}.svg "{comment_details[0]}")' 125 | ) 126 | 127 | comment_base = ( 128 | f"Build `{build_uuid}` for project `{project_name}` {comment_details[1]}" 129 | ) 130 | 131 | # Comment is in the form: 132 | # '\n\n ' 133 | comment = f"{badge_link}\n\n{comment_base}" 134 | 135 | comment += ( 136 | f"Visit the [AWS CodeBuild console]({job_url}) to view the build " 137 | f"details." 138 | ) 139 | 140 | # Add build logs to the comment for failed builds 141 | if ( 142 | build_status not in ["IN_PROGRESS", "SUCCEEDED", "STOPPED"] 143 | and log_group 144 | and log_stream 145 | ): 146 | log_params = { 147 | "logGroupName": log_group, 148 | "logStreamName": log_stream, 149 | "limit": 30, 150 | "startFromHead": False, 151 | } 152 | 153 | log.info("Sending request for CloudWatch Log events...") 154 | log.debug("GetLogEvents params:\n%s", log_params) 155 | response = cloudwatchlogs.get_log_events(**log_params) 156 | log.info("CloudWatch Log request succeeded!") 157 | log.debug("GetLogEvents response:\n%s", response) 158 | log_messages = [event["message"] for event in response["events"]] 159 | comment += f"\n```\n{''.join(log_messages)}\n```\n" 160 | 161 | pull_request_params = { 162 | "repositoryName": repo_name, 163 | "pullRequestId": pull_request_id[0], 164 | "beforeCommitId": destination_commit[0], 165 | "afterCommitId": source_commit[0], 166 | "content": comment, 167 | "clientRequestToken": request_token, 168 | } 169 | 170 | log.info("Posting comment:\n%s", comment) 171 | post_comment(pull_request_params) 172 | else: 173 | log.info("Not a pull-request build") 174 | 175 | 176 | def handle_codecommit_pull_request_event(event): 177 | """Gather pull request details and start CodeBuild job.""" 178 | params = { 179 | "projectName": PROJECT_NAME, 180 | "sourceVersion": event["detail"]["sourceCommit"], 181 | "environmentVariablesOverride": [ 182 | { 183 | "name": "FLOW_PULL_REQUEST_ID", 184 | "value": event["detail"]["pullRequestId"], 185 | "type": "PLAINTEXT", 186 | }, 187 | { 188 | "name": "FLOW_PULL_REQUEST_SRC_COMMIT", 189 | "value": event["detail"]["sourceCommit"], 190 | "type": "PLAINTEXT", 191 | }, 192 | { 193 | "name": "FLOW_PULL_REQUEST_DST_COMMIT", 194 | "value": event["detail"]["destinationCommit"], 195 | "type": "PLAINTEXT", 196 | }, 197 | ], 198 | } 199 | start_build(params) 200 | 201 | 202 | def handle_codecommit_repository_event(event): 203 | """Gather repository event details and start CodeBuild job.""" 204 | params = {"projectName": PROJECT_NAME, "sourceVersion": event["detail"]["commitId"]} 205 | if event["detail"]["referenceType"] == "branch": 206 | params["environmentVariablesOverride"] = [ 207 | { 208 | "name": "FLOW_BRANCH", 209 | "value": event["detail"]["referenceName"], 210 | "type": "PLAINTEXT", 211 | } 212 | ] 213 | elif event["detail"]["referenceType"] == "tag": 214 | params["environmentVariablesOverride"] = [ 215 | { 216 | "name": "FLOW_TAG", 217 | "value": event["detail"]["referenceName"], 218 | "type": "PLAINTEXT", 219 | } 220 | ] 221 | start_build(params) 222 | 223 | 224 | def handle_cloudwatch_schedule_event(event): 225 | """Gather event details and start CodeBuild job.""" 226 | params = { 227 | "projectName": PROJECT_NAME, 228 | "environmentVariablesOverride": [ 229 | {"name": "FLOW_SCHEDULE", "value": event["time"], "type": "PLAINTEXT"} 230 | ], 231 | } 232 | start_build(params) 233 | 234 | 235 | def review_codebuild_event(event): 236 | """Determine whether this is a valid CodeBuild review event.""" 237 | try: 238 | source = event["source"] 239 | detail_type = event["detail-type"] 240 | 241 | return ( 242 | source == "aws.codebuild" and detail_type == "CodeBuild Build State Change" 243 | ) 244 | except KeyError as exc: 245 | log.error("Caught error: %s", exc, exc_info=exc) 246 | 247 | return False 248 | 249 | 250 | def review_pull_request_event(event): 251 | """Determine whether this is a valid pull request review event.""" 252 | try: 253 | source = event["source"] 254 | detail_type = event["detail-type"] 255 | event_type = event["detail"]["event"] 256 | 257 | return ( 258 | source == "aws.codecommit" 259 | and detail_type == "CodeCommit Pull Request State Change" 260 | and event_type in ["pullRequestCreated", "pullRequestSourceBranchUpdated"] 261 | ) 262 | except KeyError as exc: 263 | log.error("Caught error: %s", exc, exc_info=exc) 264 | 265 | return False 266 | 267 | 268 | def branch_repository_event(event): 269 | """Determine whether this is a valid repository branch event.""" 270 | try: 271 | source = event["source"] 272 | detail_type = event["detail-type"] 273 | event_type = event["detail"]["event"] 274 | reference_type = event["detail"]["referenceType"] 275 | 276 | return ( 277 | source == "aws.codecommit" 278 | and detail_type == "CodeCommit Repository State Change" 279 | and event_type == "referenceUpdated" 280 | and reference_type == "branch" 281 | ) 282 | except KeyError as exc: 283 | log.error("Caught error: %s", exc, exc_info=exc) 284 | 285 | return False 286 | 287 | 288 | def tag_repository_event(event): 289 | """Determine whether this is a valid repository tag event.""" 290 | try: 291 | source = event["source"] 292 | detail_type = event["detail-type"] 293 | event_type = event["detail"]["event"] 294 | reference_type = event["detail"]["referenceType"] 295 | 296 | return ( 297 | source == "aws.codecommit" 298 | and detail_type == "CodeCommit Repository State Change" 299 | and event_type in ["referenceCreated", " referenceUpdated"] 300 | and reference_type == "tag" 301 | ) 302 | except KeyError as exc: 303 | log.error("Caught error: %s", exc, exc_info=exc) 304 | 305 | return False 306 | 307 | 308 | def schedule_cloudwatch_event(event): 309 | """Determine whether this is a valid CloudWatch scheduled event.""" 310 | try: 311 | source = event["source"] 312 | detail_type = event["detail-type"] 313 | 314 | return source == "aws.events" and detail_type == "Scheduled Event" 315 | except KeyError as exc: 316 | log.error("Caught error: %s", exc, exc_info=exc) 317 | 318 | return False 319 | 320 | 321 | def review_handler(event, context): # pylint: disable=unused-argument 322 | """Entry point for the lambda "review" handler.""" 323 | try: 324 | log.info("Received event:\n%s", json.dumps(event)) 325 | 326 | if review_codebuild_event(event): 327 | log.info("Handling valid CodeBuild review event...") 328 | handle_codebuild_review_event(event) 329 | elif review_pull_request_event(event): 330 | log.info("Handling valid pull request review event...") 331 | handle_codecommit_pull_request_event(event) 332 | else: 333 | log.info("Not a reviewable pull request or CodeBuild event") 334 | except Exception as exc: 335 | log.critical("Caught error: %s", exc, exc_info=exc) 336 | raise 337 | 338 | 339 | def branch_handler(event, context): # pylint: disable=unused-argument 340 | """Entry point for the lambda "branch" handler.""" 341 | try: 342 | log.info("Received event:\n%s", json.dumps(event)) 343 | 344 | if branch_repository_event(event): 345 | log.info("Handling valid CodeCommit branch event...") 346 | handle_codecommit_repository_event(event) 347 | else: 348 | log.info("Not a CodeCommit branch event") 349 | except Exception as exc: 350 | log.critical("Caught error: %s", exc, exc_info=exc) 351 | raise 352 | 353 | 354 | def tag_handler(event, context): # pylint: disable=unused-argument 355 | """Entry point for the lambda "tag" handler.""" 356 | try: 357 | log.info("Received event:\n%s", json.dumps(event)) 358 | 359 | if tag_repository_event(event): 360 | log.info("Handling valid CodeCommit tag event...") 361 | handle_codecommit_repository_event(event) 362 | else: 363 | log.info("Not a CodeCommit tag event") 364 | except Exception as exc: 365 | log.critical("Caught error: %s", exc, exc_info=exc) 366 | raise 367 | 368 | 369 | def schedule_handler(event, context): # pylint: disable=unused-argument 370 | """Entry point for the lambda "schedule" handler.""" 371 | try: 372 | log.info("Received event:\n%s", json.dumps(event)) 373 | 374 | if schedule_cloudwatch_event(event): 375 | log.info("Handling valid CloudWatch schedule event...") 376 | handle_cloudwatch_schedule_event(event) 377 | else: 378 | log.info("Not a CloudWatch schedule event") 379 | except Exception as exc: 380 | log.critical("Caught error: %s", exc, exc_info=exc) 381 | raise 382 | -------------------------------------------------------------------------------- /modules/_internal/handler/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.12" 3 | } 4 | 5 | locals { 6 | name_slug = "${var.name_prefix}${var.repo_name}-${var.stage}-flow-ci" 7 | project_name = element(split("/", element(split(":", var.project_arn), 5)), 1) 8 | } 9 | 10 | data "aws_iam_policy_document" "lambda" { 11 | override_policy_documents = compact([var.lambda_policy_override]) 12 | 13 | statement { 14 | actions = ["codebuild:StartBuild"] 15 | resources = [var.project_arn] 16 | } 17 | } 18 | 19 | module "handler" { 20 | source = "git::https://github.com/plus3it/terraform-aws-lambda.git?ref=v1.3.0" 21 | 22 | function_name = local.name_slug 23 | description = var.stage_description 24 | handler = "lambda.${var.handler}" 25 | runtime = var.python_runtime 26 | timeout = 300 27 | 28 | // Specify a file or directory for the source code. 29 | source_path = "${path.module}/lambda.py" 30 | 31 | // Attach a policy. 32 | policy = { 33 | json = data.aws_iam_policy_document.lambda.json 34 | } 35 | 36 | // Add environment variables. 37 | environment = { 38 | variables = { 39 | PROJECT_NAME = local.project_name 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /modules/_internal/handler/outputs.tf: -------------------------------------------------------------------------------- 1 | output "function_arn" { 2 | description = "The ARN of the Lambda function" 3 | value = module.handler.function_arn 4 | } 5 | 6 | output "function_name" { 7 | description = "The name of the Lambda function" 8 | value = module.handler.function_name 9 | } 10 | 11 | output "role_arn" { 12 | description = "The ARN of the IAM role created for the Lambda function" 13 | value = module.handler.role_arn 14 | } 15 | 16 | output "role_name" { 17 | description = "The name of the IAM role created for the Lambda function" 18 | value = module.handler.role_name 19 | } 20 | -------------------------------------------------------------------------------- /modules/_internal/handler/variables.tf: -------------------------------------------------------------------------------- 1 | variable "handler" { 2 | type = string 3 | description = "Entry point for the lambda function. Must be one of: review_handler, branch_handler, tag_handler, schedule_handler" 4 | } 5 | 6 | variable "stage" { 7 | type = string 8 | description = "Name of the stage" 9 | } 10 | 11 | variable "stage_description" { 12 | type = string 13 | description = "Description of the stage, used in resources created by this module" 14 | } 15 | 16 | variable "repo_name" { 17 | type = string 18 | description = "Name of the CodeCommit repository" 19 | } 20 | 21 | variable "name_prefix" { 22 | type = string 23 | description = "Prefix to attach to repo name" 24 | default = "" 25 | nullable = false 26 | } 27 | 28 | variable "project_arn" { 29 | type = string 30 | description = "ARN of the CodeBuild project" 31 | } 32 | 33 | variable "lambda_policy_override" { 34 | type = string 35 | description = "IAM policy document in JSON that extends the Lambda service role" 36 | default = "" 37 | } 38 | 39 | variable "python_runtime" { 40 | type = string 41 | description = "Python runtime for the Lambda function" 42 | default = "python3.9" 43 | nullable = false 44 | } 45 | -------------------------------------------------------------------------------- /modules/_internal/runner/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.12" 3 | } 4 | 5 | data "aws_partition" "current" {} 6 | 7 | data "aws_caller_identity" "current" {} 8 | 9 | data "aws_region" "current" {} 10 | 11 | locals { 12 | account_id = data.aws_caller_identity.current.account_id 13 | partition = data.aws_partition.current.partition 14 | region = data.aws_region.current.name 15 | } 16 | 17 | locals { 18 | name_slug = "${var.name_prefix}${var.repo_name}-${var.stage}-flow-ci" 19 | log_group_arn = "arn:${local.partition}:logs:${local.region}:${local.account_id}:log-group:/aws/codebuild/${local.name_slug}" 20 | repo_arn = "arn:${local.partition}:codecommit:${local.region}:${local.account_id}:${var.repo_name}" 21 | repo_url = "https://git-codecommit.${local.region}.amazonaws.com/v1/repos/${var.repo_name}" 22 | 23 | 24 | } 25 | 26 | locals { 27 | # Setup the CodeBuild environment object 28 | default_environment = { 29 | compute_type = "BUILD_GENERAL1_SMALL" 30 | image = "aws/codebuild/standard:4.0" 31 | type = "LINUX_CONTAINER" 32 | } 33 | 34 | environment = merge(local.default_environment, var.environment) 35 | } 36 | 37 | locals { 38 | # Setup the CodeBuild artifacts object 39 | default_artifacts = { 40 | type = "NO_ARTIFACTS" 41 | } 42 | 43 | artifacts = merge(local.default_artifacts, var.artifacts) 44 | } 45 | 46 | # IAM resources for CodeBuild 47 | 48 | data "template_file" "codebuild_policy_override" { 49 | template = var.policy_override 50 | 51 | vars = { 52 | name = "${var.name_prefix}${var.repo_name}" 53 | name_prefix = var.name_prefix 54 | name_slug = local.name_slug 55 | partition = data.aws_partition.current.partition 56 | region = data.aws_region.current.name 57 | repo_name = var.repo_name 58 | account_id = data.aws_caller_identity.current.account_id 59 | } 60 | } 61 | 62 | data "template_file" "policy_arns" { 63 | count = var.policy_arns != null ? length(var.policy_arns) : 0 64 | 65 | template = var.policy_arns[count.index] 66 | 67 | vars = { 68 | name = "${var.name_prefix}${var.repo_name}" 69 | name_prefix = var.name_prefix 70 | name_slug = local.name_slug 71 | partition = data.aws_partition.current.partition 72 | region = data.aws_region.current.name 73 | repo_name = var.repo_name 74 | account_id = data.aws_caller_identity.current.account_id 75 | } 76 | } 77 | 78 | data "aws_iam_policy_document" "codebuild_assume_role" { 79 | statement { 80 | actions = ["sts:AssumeRole"] 81 | 82 | principals { 83 | type = "Service" 84 | identifiers = ["codebuild.amazonaws.com"] 85 | } 86 | } 87 | } 88 | 89 | data "aws_iam_policy_document" "codebuild" { 90 | override_policy_documents = compact([data.template_file.codebuild_policy_override.rendered]) 91 | 92 | statement { 93 | actions = [ 94 | "logs:CreateLogGroup", 95 | "logs:CreateLogStream", 96 | "logs:PutLogEvents", 97 | ] 98 | 99 | resources = [ 100 | local.log_group_arn, 101 | "${local.log_group_arn}:*", 102 | ] 103 | } 104 | 105 | statement { 106 | actions = ["codecommit:GitPull"] 107 | resources = [local.repo_arn] 108 | } 109 | 110 | dynamic "statement" { 111 | for_each = var.vpc_config != null ? [var.vpc_config] : [] 112 | content { 113 | actions = [ 114 | "ec2:CreateNetworkInterface", 115 | "ec2:DescribeDhcpOptions", 116 | "ec2:DescribeNetworkInterfaces", 117 | "ec2:DeleteNetworkInterface", 118 | "ec2:DescribeSubnets", 119 | "ec2:DescribeSecurityGroups", 120 | "ec2:DescribeVpcs", 121 | ] 122 | resources = ["*"] 123 | } 124 | } 125 | 126 | dynamic "statement" { 127 | for_each = var.vpc_config != null ? [var.vpc_config] : [] 128 | content { 129 | actions = [ 130 | "ec2:CreateNetworkInterfacePermission", 131 | ] 132 | resources = ["arn:${local.partition}:ec2:${local.region}:${local.account_id}:network-interface/*"] 133 | 134 | condition { 135 | test = "StringEquals" 136 | variable = "ec2:Subnet" 137 | 138 | values = [ 139 | for subnet in statement.value.subnets : 140 | "arn:${local.partition}:ec2:${local.region}:${local.account_id}:subnet/${subnet}" 141 | ] 142 | } 143 | 144 | condition { 145 | test = "StringEquals" 146 | variable = "ec2:AuthorizedService" 147 | 148 | values = [ 149 | "codebuild.amazonaws.com", 150 | ] 151 | } 152 | } 153 | } 154 | } 155 | 156 | resource "aws_iam_role" "codebuild" { 157 | name_prefix = "flow-ci-codebuild-service-role-" 158 | description = "${local.name_slug}-codebuild-service-role -- Managed by Terraform" 159 | assume_role_policy = data.aws_iam_policy_document.codebuild_assume_role.json 160 | managed_policy_arns = var.policy_arns != null ? data.template_file.policy_arns[*].rendered : null 161 | inline_policy { 162 | name = "${local.name_slug}-codebuild" 163 | policy = data.aws_iam_policy_document.codebuild.json 164 | } 165 | } 166 | 167 | # CodeBuild Resources 168 | resource "aws_codebuild_project" "this" { 169 | name = local.name_slug 170 | description = var.stage_description 171 | service_role = aws_iam_role.codebuild.arn 172 | 173 | badge_enabled = var.badge_enabled 174 | build_timeout = var.build_timeout 175 | queued_timeout = var.queued_timeout 176 | encryption_key = var.encryption_key 177 | source_version = var.source_version 178 | tags = var.tags 179 | 180 | dynamic "artifacts" { 181 | for_each = [local.artifacts] 182 | content { 183 | encryption_disabled = lookup(artifacts.value, "encryption_disabled", null) 184 | location = lookup(artifacts.value, "location", null) 185 | name = lookup(artifacts.value, "name", null) 186 | namespace_type = lookup(artifacts.value, "namespace_type", null) 187 | packaging = lookup(artifacts.value, "packaging", null) 188 | path = lookup(artifacts.value, "path", null) 189 | type = artifacts.value.type 190 | } 191 | } 192 | 193 | dynamic "environment" { 194 | for_each = [local.environment] 195 | content { 196 | certificate = lookup(environment.value, "certificate", null) 197 | compute_type = environment.value.compute_type 198 | image = environment.value.image 199 | image_pull_credentials_type = lookup(environment.value, "image_pull_credentials_type", null) 200 | privileged_mode = lookup(environment.value, "privileged_mode", null) 201 | type = environment.value.type 202 | 203 | dynamic "environment_variable" { 204 | for_each = var.environment_variables 205 | content { 206 | name = environment_variable.value.name 207 | type = lookup(environment_variable.value, "type", null) 208 | value = environment_variable.value.value 209 | } 210 | } 211 | } 212 | } 213 | 214 | dynamic "vpc_config" { 215 | for_each = var.vpc_config != null ? [var.vpc_config] : [] 216 | content { 217 | security_group_ids = vpc_config.value.security_group_ids 218 | subnets = vpc_config.value.subnets 219 | vpc_id = vpc_config.value.vpc_id 220 | } 221 | } 222 | 223 | source { 224 | type = "CODECOMMIT" 225 | location = local.repo_url 226 | buildspec = var.buildspec 227 | } 228 | 229 | lifecycle { 230 | ignore_changes = [project_visibility] 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /modules/_internal/runner/outputs.tf: -------------------------------------------------------------------------------- 1 | output "codebuild_project_arn" { 2 | description = "ARN of the CodeBuild Project" 3 | value = aws_codebuild_project.this.arn 4 | } 5 | 6 | output "codebuild_project_name" { 7 | description = "Name of the CodeBuild Project" 8 | value = aws_codebuild_project.this.name 9 | } 10 | 11 | output "codebuild_project_service_role" { 12 | description = "ARN of the CodeBuild Project Service Role" 13 | value = aws_codebuild_project.this.service_role 14 | } 15 | -------------------------------------------------------------------------------- /modules/_internal/runner/variables.tf: -------------------------------------------------------------------------------- 1 | variable "stage" { 2 | type = string 3 | description = "Name of the stage" 4 | } 5 | 6 | variable "stage_description" { 7 | type = string 8 | description = "Description of the stage, used in resources created by this module" 9 | } 10 | 11 | variable "repo_name" { 12 | type = string 13 | description = "Name of the CodeCommit repository" 14 | } 15 | 16 | variable "name_prefix" { 17 | type = string 18 | description = "Prefix to attach to repo name" 19 | default = "" 20 | nullable = false 21 | } 22 | 23 | variable "buildspec" { 24 | type = string 25 | description = "Buildspec used when the release branch is updated" 26 | default = null 27 | } 28 | 29 | variable "badge_enabled" { 30 | type = bool 31 | description = "Generates a publicly-accessible URL for the projects build badge" 32 | default = null 33 | } 34 | 35 | variable "build_timeout" { 36 | type = number 37 | description = "How long in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait until timing out any related build that does not get marked as completed" 38 | default = null 39 | } 40 | 41 | variable "queued_timeout" { 42 | type = number 43 | description = "How long in minutes, from 5 to 480 (8 hours), a build is allowed to be queued before it times out" 44 | default = null 45 | } 46 | 47 | variable "encryption_key" { 48 | type = string 49 | description = "The AWS Key Management Service (AWS KMS) customer master key (CMK) to be used for encrypting the build project's build output artifacts" 50 | default = null 51 | } 52 | 53 | variable "source_version" { 54 | type = string 55 | description = "A version of the build input to be built for this project. If not specified, the latest version is used" 56 | default = null 57 | } 58 | 59 | variable "tags" { 60 | type = map(string) 61 | description = "A map of tags to assign to the resource" 62 | default = {} 63 | } 64 | 65 | variable "artifacts" { 66 | type = map(string) 67 | description = "Map defining an artifacts object for the CodeBuild job" 68 | } 69 | 70 | variable "environment" { 71 | type = map(string) 72 | description = "Map describing the environment object for the CodeBuild job" 73 | } 74 | 75 | variable "environment_variables" { 76 | type = list(map(string)) 77 | description = "List of environment variable map objects for the CodeBuild job" 78 | } 79 | 80 | variable "vpc_config" { 81 | description = "Object of inputs for the VPC configuration of the CodeBuild job" 82 | type = object({ 83 | security_group_ids = list(string) 84 | subnets = list(string) 85 | vpc_id = string 86 | }) 87 | default = null 88 | } 89 | 90 | variable "policy_arns" { 91 | type = list(string) 92 | description = "List of IAM policy ARNs to attach to the CodeBuild service role" 93 | } 94 | 95 | variable "policy_override" { 96 | type = string 97 | description = "IAM policy document in JSON that extends the basic inline CodeBuild service role" 98 | } 99 | -------------------------------------------------------------------------------- /modules/_internal/trigger/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.12" 3 | } 4 | 5 | locals { 6 | name_slug = "${var.name_prefix}${var.repo_name}-${var.stage}-flow-ci" 7 | } 8 | 9 | # CloudWatch Event Resources 10 | 11 | resource "aws_cloudwatch_event_rule" "this" { 12 | name = local.name_slug 13 | description = var.stage_description 14 | event_pattern = var.event_pattern 15 | schedule_expression = var.schedule_expression 16 | } 17 | 18 | resource "aws_cloudwatch_event_target" "this" { 19 | rule = aws_cloudwatch_event_rule.this.name 20 | arn = var.target_arn 21 | } 22 | -------------------------------------------------------------------------------- /modules/_internal/trigger/outputs.tf: -------------------------------------------------------------------------------- 1 | output "events_rule_arn" { 2 | description = "ARN of the CloudWatch Event Rule" 3 | value = aws_cloudwatch_event_rule.this.arn 4 | } 5 | -------------------------------------------------------------------------------- /modules/_internal/trigger/variables.tf: -------------------------------------------------------------------------------- 1 | variable "stage" { 2 | type = string 3 | description = "Name of the stage" 4 | } 5 | 6 | variable "stage_description" { 7 | type = string 8 | description = "Description of the stage, used in resources created by this module" 9 | } 10 | 11 | variable "target_arn" { 12 | type = string 13 | description = "ARN of the resource that the event will trigger" 14 | } 15 | 16 | variable "repo_name" { 17 | type = string 18 | description = "Name of the CodeCommit repository" 19 | } 20 | 21 | variable "name_prefix" { 22 | type = string 23 | description = "Prefix to attach to repo name" 24 | default = "" 25 | nullable = false 26 | } 27 | 28 | variable "event_pattern" { 29 | type = string 30 | description = "CloudWatch Event pattern that triggers the CodeBuild job" 31 | default = "" 32 | } 33 | 34 | variable "schedule_expression" { 35 | type = string 36 | description = "CloudWatch Event schedule that triggers the CodeBuild job" 37 | default = "" 38 | } 39 | -------------------------------------------------------------------------------- /modules/branch/README.md: -------------------------------------------------------------------------------- 1 | # terraform-aws-codecommit-flow-ci/branch 2 | 3 | Trigger a build when a CodeCommit branch is updated. 4 | 5 | 6 | ## Requirements 7 | 8 | | Name | Version | 9 | |------|---------| 10 | | [terraform](#requirement\_terraform) | >= 0.12 | 11 | 12 | ## Providers 13 | 14 | | Name | Version | 15 | |------|---------| 16 | | [aws](#provider\_aws) | n/a | 17 | 18 | ## Resources 19 | 20 | | Name | Type | 21 | |------|------| 22 | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 23 | | [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | 24 | | [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | 25 | 26 | ## Inputs 27 | 28 | | Name | Description | Type | Default | Required | 29 | |------|-------------|------|---------|:--------:| 30 | | [repo\_name](#input\_repo\_name) | Name of the CodeCommit repository | `string` | n/a | yes | 31 | | [artifacts](#input\_artifacts) | Map defining an artifacts object for the CodeBuild job | `map(string)` | `{}` | no | 32 | | [badge\_enabled](#input\_badge\_enabled) | Generates a publicly-accessible URL for the projects build badge | `bool` | `null` | no | 33 | | [branch](#input\_branch) | Name of the branch where updates will trigger a build | `string` | `"master"` | no | 34 | | [build\_timeout](#input\_build\_timeout) | How long in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait until timing out any related build that does not get marked as completed | `number` | `null` | no | 35 | | [buildspec](#input\_buildspec) | Buildspec used when the specified branch is updated | `string` | `""` | no | 36 | | [encryption\_key](#input\_encryption\_key) | The AWS Key Management Service (AWS KMS) customer master key (CMK) to be used for encrypting the build project's build output artifacts | `string` | `null` | no | 37 | | [environment](#input\_environment) | Map describing the environment object for the CodeBuild job | `map(string)` | `{}` | no | 38 | | [environment\_variables](#input\_environment\_variables) | List of environment variable map objects for the CodeBuild job | `list(map(string))` | `[]` | no | 39 | | [name\_prefix](#input\_name\_prefix) | Prefix to attach to repo name | `string` | `""` | no | 40 | | [policy\_arns](#input\_policy\_arns) | List of IAM policy ARNs to attach to the CodeBuild service role or null to support ignoring externally attached policies | `list(string)` | `[]` | no | 41 | | [policy\_override](#input\_policy\_override) | IAM policy document in JSON that extends the basic inline CodeBuild service role | `string` | `""` | no | 42 | | [python\_runtime](#input\_python\_runtime) | Python runtime for the handler Lambda function | `string` | `null` | no | 43 | | [queued\_timeout](#input\_queued\_timeout) | How long in minutes, from 5 to 480 (8 hours), a build is allowed to be queued before it times out | `number` | `null` | no | 44 | | [source\_version](#input\_source\_version) | A version of the build input to be built for this project. If not specified, the latest version is used | `string` | `null` | no | 45 | | [tags](#input\_tags) | A map of tags to assign to the resource | `map(string)` | `{}` | no | 46 | | [vpc\_config](#input\_vpc\_config) | Object of inputs for the VPC configuration of the CodeBuild job |
object({
security_group_ids = list(string)
subnets = list(string)
vpc_id = string
})
| `null` | no | 47 | 48 | ## Outputs 49 | 50 | | Name | Description | 51 | |------|-------------| 52 | | [codebuild\_project\_arn](#output\_codebuild\_project\_arn) | ARN of the CodeBuild Project | 53 | | [codebuild\_project\_name](#output\_codebuild\_project\_name) | Name of the CodeBuild Project | 54 | | [codebuild\_project\_service\_role](#output\_codebuild\_project\_service\_role) | ARN of the CodeBuild Project Service Role | 55 | | [events\_rule\_codebuild\_arn](#output\_events\_rule\_codebuild\_arn) | ARN of the CloudWatch Event Rule for CodeBuild | 56 | | [lambda\_function\_arn](#output\_lambda\_function\_arn) | ARN of the Lambda function | 57 | | [lambda\_function\_name](#output\_lambda\_function\_name) | Name of the Lambda function | 58 | | [lambda\_role\_arn](#output\_lambda\_role\_arn) | ARN of the Lambda IAM Role | 59 | | [lambda\_role\_name](#output\_lambda\_role\_name) | Name of the Lambda IAM Role | 60 | 61 | 62 | -------------------------------------------------------------------------------- /modules/branch/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.12" 3 | } 4 | 5 | data "aws_partition" "current" {} 6 | 7 | data "aws_caller_identity" "current" {} 8 | 9 | data "aws_region" "current" {} 10 | 11 | locals { 12 | stage = "branch-${replace(var.branch, "/", "")}" 13 | stage_description = "Execute a job/buildspec when the ${var.branch} branch is updated in ${var.repo_name}" 14 | repo_arn = "arn:${data.aws_partition.current.partition}:codecommit:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:${var.repo_name}" 15 | 16 | event_pattern = jsonencode( 17 | { 18 | "detail-type" : ["CodeCommit Repository State Change"], 19 | "source" : ["aws.codecommit"], 20 | "resources" : [local.repo_arn], 21 | "detail" : { 22 | "event" : ["referenceUpdated"], 23 | "repositoryName" : [var.repo_name], 24 | "referenceType" : ["branch"], 25 | "referenceName" : [var.branch] 26 | } 27 | } 28 | ) 29 | } 30 | 31 | module "handler" { 32 | source = "../_internal/handler" 33 | 34 | handler = "branch_handler" 35 | name_prefix = var.name_prefix 36 | repo_name = var.repo_name 37 | stage = local.stage 38 | stage_description = local.stage_description 39 | project_arn = module.runner.codebuild_project_arn 40 | python_runtime = var.python_runtime 41 | } 42 | 43 | module "runner" { 44 | source = "../_internal/runner" 45 | 46 | name_prefix = var.name_prefix 47 | repo_name = var.repo_name 48 | stage = local.stage 49 | stage_description = local.stage_description 50 | buildspec = var.buildspec 51 | artifacts = var.artifacts 52 | environment = var.environment 53 | environment_variables = var.environment_variables 54 | policy_override = var.policy_override 55 | policy_arns = var.policy_arns 56 | vpc_config = var.vpc_config 57 | 58 | badge_enabled = var.badge_enabled 59 | build_timeout = var.build_timeout 60 | queued_timeout = var.queued_timeout 61 | encryption_key = var.encryption_key 62 | source_version = var.source_version 63 | tags = var.tags 64 | } 65 | 66 | module "trigger" { 67 | source = "../_internal/trigger" 68 | 69 | name_prefix = var.name_prefix 70 | repo_name = var.repo_name 71 | stage = local.stage 72 | stage_description = local.stage_description 73 | target_arn = module.handler.function_arn 74 | event_pattern = local.event_pattern 75 | } 76 | 77 | resource "aws_lambda_permission" "trigger" { 78 | action = "lambda:InvokeFunction" 79 | function_name = module.handler.function_name 80 | principal = "events.amazonaws.com" 81 | source_arn = module.trigger.events_rule_arn 82 | } 83 | -------------------------------------------------------------------------------- /modules/branch/outputs.tf: -------------------------------------------------------------------------------- 1 | output "codebuild_project_arn" { 2 | description = "ARN of the CodeBuild Project" 3 | value = module.runner.codebuild_project_arn 4 | } 5 | 6 | output "codebuild_project_name" { 7 | description = "Name of the CodeBuild Project" 8 | value = module.runner.codebuild_project_name 9 | } 10 | 11 | output "codebuild_project_service_role" { 12 | description = "ARN of the CodeBuild Project Service Role" 13 | value = module.runner.codebuild_project_service_role 14 | } 15 | 16 | output "events_rule_codebuild_arn" { 17 | description = "ARN of the CloudWatch Event Rule for CodeBuild" 18 | value = module.trigger.events_rule_arn 19 | } 20 | 21 | output "lambda_function_arn" { 22 | description = "ARN of the Lambda function" 23 | value = module.handler.function_arn 24 | } 25 | 26 | output "lambda_function_name" { 27 | description = "Name of the Lambda function" 28 | value = module.handler.function_name 29 | } 30 | 31 | output "lambda_role_arn" { 32 | description = "ARN of the Lambda IAM Role" 33 | value = module.handler.role_arn 34 | } 35 | 36 | output "lambda_role_name" { 37 | description = "Name of the Lambda IAM Role" 38 | value = module.handler.function_arn 39 | } 40 | -------------------------------------------------------------------------------- /modules/branch/variables.tf: -------------------------------------------------------------------------------- 1 | variable "branch" { 2 | type = string 3 | description = "Name of the branch where updates will trigger a build" 4 | default = "master" 5 | nullable = false 6 | } 7 | 8 | variable "repo_name" { 9 | type = string 10 | description = "Name of the CodeCommit repository" 11 | } 12 | 13 | variable "name_prefix" { 14 | type = string 15 | description = "Prefix to attach to repo name" 16 | default = "" 17 | nullable = false 18 | } 19 | 20 | variable "buildspec" { 21 | type = string 22 | description = "Buildspec used when the specified branch is updated" 23 | default = "" 24 | } 25 | 26 | variable "badge_enabled" { 27 | type = bool 28 | description = "Generates a publicly-accessible URL for the projects build badge" 29 | default = null 30 | } 31 | 32 | variable "build_timeout" { 33 | type = number 34 | description = "How long in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait until timing out any related build that does not get marked as completed" 35 | default = null 36 | } 37 | 38 | variable "queued_timeout" { 39 | type = number 40 | description = "How long in minutes, from 5 to 480 (8 hours), a build is allowed to be queued before it times out" 41 | default = null 42 | } 43 | 44 | variable "encryption_key" { 45 | type = string 46 | description = "The AWS Key Management Service (AWS KMS) customer master key (CMK) to be used for encrypting the build project's build output artifacts" 47 | default = null 48 | } 49 | 50 | variable "source_version" { 51 | type = string 52 | description = "A version of the build input to be built for this project. If not specified, the latest version is used" 53 | default = null 54 | } 55 | 56 | variable "tags" { 57 | type = map(string) 58 | description = "A map of tags to assign to the resource" 59 | default = {} 60 | } 61 | 62 | variable "artifacts" { 63 | type = map(string) 64 | description = "Map defining an artifacts object for the CodeBuild job" 65 | default = {} 66 | } 67 | 68 | variable "environment" { 69 | type = map(string) 70 | description = "Map describing the environment object for the CodeBuild job" 71 | default = {} 72 | } 73 | 74 | variable "environment_variables" { 75 | type = list(map(string)) 76 | description = "List of environment variable map objects for the CodeBuild job" 77 | default = [] 78 | } 79 | 80 | variable "vpc_config" { 81 | description = "Object of inputs for the VPC configuration of the CodeBuild job" 82 | type = object({ 83 | security_group_ids = list(string) 84 | subnets = list(string) 85 | vpc_id = string 86 | }) 87 | default = null 88 | } 89 | 90 | variable "policy_arns" { 91 | type = list(string) 92 | description = "List of IAM policy ARNs to attach to the CodeBuild service role or null to support ignoring externally attached policies" 93 | default = [] 94 | } 95 | 96 | variable "policy_override" { 97 | type = string 98 | description = "IAM policy document in JSON that extends the basic inline CodeBuild service role" 99 | default = "" 100 | } 101 | 102 | variable "python_runtime" { 103 | type = string 104 | description = "Python runtime for the handler Lambda function" 105 | default = null 106 | } 107 | -------------------------------------------------------------------------------- /modules/on-demand/README.md: -------------------------------------------------------------------------------- 1 | # terraform-aws-codecommit-flow-ci/on-demand 2 | 3 | Trigger a build on demand. 4 | 5 | 6 | ## Requirements 7 | 8 | | Name | Version | 9 | |------|---------| 10 | | [terraform](#requirement\_terraform) | >= 0.12 | 11 | 12 | ## Providers 13 | 14 | No providers. 15 | 16 | ## Resources 17 | 18 | No resources. 19 | 20 | ## Inputs 21 | 22 | | Name | Description | Type | Default | Required | 23 | |------|-------------|------|---------|:--------:| 24 | | [repo\_name](#input\_repo\_name) | Name of the CodeCommit repository | `string` | n/a | yes | 25 | | [artifacts](#input\_artifacts) | Map defining an artifacts object for the CodeBuild job | `map(string)` | `{}` | no | 26 | | [badge\_enabled](#input\_badge\_enabled) | Generates a publicly-accessible URL for the projects build badge | `bool` | `null` | no | 27 | | [build\_timeout](#input\_build\_timeout) | How long in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait until timing out any related build that does not get marked as completed | `number` | `null` | no | 28 | | [buildspec](#input\_buildspec) | Buildspec used when a tag reference is created or updated | `string` | `""` | no | 29 | | [encryption\_key](#input\_encryption\_key) | The AWS Key Management Service (AWS KMS) customer master key (CMK) to be used for encrypting the build project's build output artifacts | `string` | `null` | no | 30 | | [environment](#input\_environment) | Map describing the environment object for the CodeBuild job | `map(string)` | `{}` | no | 31 | | [environment\_variables](#input\_environment\_variables) | List of environment variable map objects for the CodeBuild job | `list(map(string))` | `[]` | no | 32 | | [name\_prefix](#input\_name\_prefix) | Prefix to attach to repo name | `string` | `""` | no | 33 | | [policy\_arns](#input\_policy\_arns) | List of IAM policy ARNs to attach to the CodeBuild service role or null to support ignoring externally attached policies | `list(string)` | `[]` | no | 34 | | [policy\_override](#input\_policy\_override) | IAM policy document in JSON that extends the basic inline CodeBuild service role | `string` | `""` | no | 35 | | [queued\_timeout](#input\_queued\_timeout) | How long in minutes, from 5 to 480 (8 hours), a build is allowed to be queued before it times out | `number` | `null` | no | 36 | | [source\_version](#input\_source\_version) | A version of the build input to be built for this project. If not specified, the latest version is used | `string` | `null` | no | 37 | | [tags](#input\_tags) | A map of tags to assign to the resource | `map(string)` | `{}` | no | 38 | | [vpc\_config](#input\_vpc\_config) | Object of inputs for the VPC configuration of the CodeBuild job |
object({
security_group_ids = list(string)
subnets = list(string)
vpc_id = string
})
| `null` | no | 39 | 40 | ## Outputs 41 | 42 | | Name | Description | 43 | |------|-------------| 44 | | [codebuild\_project\_arn](#output\_codebuild\_project\_arn) | ARN of the CodeBuild Project | 45 | | [codebuild\_project\_name](#output\_codebuild\_project\_name) | Name of the CodeBuild Project | 46 | | [codebuild\_project\_service\_role](#output\_codebuild\_project\_service\_role) | ARN of the CodeBuild Project Service Role | 47 | 48 | 49 | -------------------------------------------------------------------------------- /modules/on-demand/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.12" 3 | } 4 | 5 | locals { 6 | stage = "on-demand" 7 | stage_description = "Execute a job/buildspec on demand" 8 | } 9 | 10 | module "runner" { 11 | source = "../_internal/runner" 12 | 13 | name_prefix = var.name_prefix 14 | repo_name = var.repo_name 15 | stage = local.stage 16 | stage_description = local.stage_description 17 | buildspec = var.buildspec 18 | artifacts = var.artifacts 19 | environment = var.environment 20 | environment_variables = var.environment_variables 21 | policy_override = var.policy_override 22 | policy_arns = var.policy_arns 23 | vpc_config = var.vpc_config 24 | 25 | badge_enabled = var.badge_enabled 26 | build_timeout = var.build_timeout 27 | queued_timeout = var.queued_timeout 28 | encryption_key = var.encryption_key 29 | source_version = var.source_version 30 | tags = var.tags 31 | } 32 | -------------------------------------------------------------------------------- /modules/on-demand/outputs.tf: -------------------------------------------------------------------------------- 1 | output "codebuild_project_arn" { 2 | description = "ARN of the CodeBuild Project" 3 | value = module.runner.codebuild_project_arn 4 | } 5 | 6 | output "codebuild_project_name" { 7 | description = "Name of the CodeBuild Project" 8 | value = module.runner.codebuild_project_name 9 | } 10 | 11 | output "codebuild_project_service_role" { 12 | description = "ARN of the CodeBuild Project Service Role" 13 | value = module.runner.codebuild_project_service_role 14 | } 15 | -------------------------------------------------------------------------------- /modules/on-demand/variables.tf: -------------------------------------------------------------------------------- 1 | variable "repo_name" { 2 | type = string 3 | description = "Name of the CodeCommit repository" 4 | } 5 | 6 | variable "name_prefix" { 7 | type = string 8 | description = "Prefix to attach to repo name" 9 | default = "" 10 | nullable = false 11 | } 12 | 13 | variable "buildspec" { 14 | type = string 15 | description = "Buildspec used when a tag reference is created or updated" 16 | default = "" 17 | } 18 | 19 | variable "badge_enabled" { 20 | type = bool 21 | description = "Generates a publicly-accessible URL for the projects build badge" 22 | default = null 23 | } 24 | 25 | variable "build_timeout" { 26 | type = number 27 | description = "How long in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait until timing out any related build that does not get marked as completed" 28 | default = null 29 | } 30 | 31 | variable "queued_timeout" { 32 | type = number 33 | description = "How long in minutes, from 5 to 480 (8 hours), a build is allowed to be queued before it times out" 34 | default = null 35 | } 36 | 37 | variable "encryption_key" { 38 | type = string 39 | description = "The AWS Key Management Service (AWS KMS) customer master key (CMK) to be used for encrypting the build project's build output artifacts" 40 | default = null 41 | } 42 | 43 | variable "source_version" { 44 | type = string 45 | description = "A version of the build input to be built for this project. If not specified, the latest version is used" 46 | default = null 47 | } 48 | 49 | variable "tags" { 50 | type = map(string) 51 | description = "A map of tags to assign to the resource" 52 | default = {} 53 | } 54 | 55 | variable "artifacts" { 56 | type = map(string) 57 | description = "Map defining an artifacts object for the CodeBuild job" 58 | default = {} 59 | } 60 | 61 | variable "environment" { 62 | type = map(string) 63 | description = "Map describing the environment object for the CodeBuild job" 64 | default = {} 65 | } 66 | 67 | variable "environment_variables" { 68 | type = list(map(string)) 69 | description = "List of environment variable map objects for the CodeBuild job" 70 | default = [] 71 | } 72 | 73 | variable "vpc_config" { 74 | description = "Object of inputs for the VPC configuration of the CodeBuild job" 75 | type = object({ 76 | security_group_ids = list(string) 77 | subnets = list(string) 78 | vpc_id = string 79 | }) 80 | default = null 81 | } 82 | 83 | variable "policy_arns" { 84 | type = list(string) 85 | description = "List of IAM policy ARNs to attach to the CodeBuild service role or null to support ignoring externally attached policies" 86 | default = [] 87 | } 88 | 89 | variable "policy_override" { 90 | type = string 91 | description = "IAM policy document in JSON that extends the basic inline CodeBuild service role" 92 | default = "" 93 | } 94 | -------------------------------------------------------------------------------- /modules/review/README.md: -------------------------------------------------------------------------------- 1 | # terraform-aws-codecommit-flow-ci/review 2 | 3 | Trigger a build when a CodeCommit pull request is updated. 4 | 5 | 6 | ## Requirements 7 | 8 | | Name | Version | 9 | |------|---------| 10 | | [terraform](#requirement\_terraform) | >= 0.12 | 11 | 12 | ## Providers 13 | 14 | | Name | Version | 15 | |------|---------| 16 | | [aws](#provider\_aws) | n/a | 17 | 18 | ## Resources 19 | 20 | | Name | Type | 21 | |------|------| 22 | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | 23 | | [aws_iam_policy_document.handler](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | 24 | | [aws_partition.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/partition) | data source | 25 | | [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source | 26 | 27 | ## Inputs 28 | 29 | | Name | Description | Type | Default | Required | 30 | |------|-------------|------|---------|:--------:| 31 | | [repo\_name](#input\_repo\_name) | Name of the CodeCommit repository | `string` | n/a | yes | 32 | | [artifacts](#input\_artifacts) | Map defining an artifacts object for the CodeBuild job | `map(string)` | `{}` | no | 33 | | [badge\_enabled](#input\_badge\_enabled) | Generates a publicly-accessible URL for the projects build badge | `bool` | `null` | no | 34 | | [build\_timeout](#input\_build\_timeout) | How long in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait until timing out any related build that does not get marked as completed | `number` | `null` | no | 35 | | [buildspec](#input\_buildspec) | Buildspec used when a pull request is created or updated | `string` | `""` | no | 36 | | [encryption\_key](#input\_encryption\_key) | The AWS Key Management Service (AWS KMS) customer master key (CMK) to be used for encrypting the build project's build output artifacts | `string` | `null` | no | 37 | | [environment](#input\_environment) | Map describing the environment object for the CodeBuild job | `map(string)` | `{}` | no | 38 | | [environment\_variables](#input\_environment\_variables) | List of environment variable map objects for the CodeBuild job | `list(map(string))` | `[]` | no | 39 | | [name\_prefix](#input\_name\_prefix) | Prefix to attach to repo name | `string` | `""` | no | 40 | | [policy\_arns](#input\_policy\_arns) | List of IAM policy ARNs to attach to the CodeBuild service role or null to support ignoring externally attached policies | `list(string)` | `[]` | no | 41 | | [policy\_override](#input\_policy\_override) | IAM policy document in JSON that extends the basic inline CodeBuild service role | `string` | `""` | no | 42 | | [python\_runtime](#input\_python\_runtime) | Python runtime for the handler Lambda function | `string` | `null` | no | 43 | | [queued\_timeout](#input\_queued\_timeout) | How long in minutes, from 5 to 480 (8 hours), a build is allowed to be queued before it times out | `number` | `null` | no | 44 | | [source\_version](#input\_source\_version) | A version of the build input to be built for this project. If not specified, the latest version is used | `string` | `null` | no | 45 | | [tags](#input\_tags) | A map of tags to assign to the resource | `map(string)` | `{}` | no | 46 | | [vpc\_config](#input\_vpc\_config) | Object of inputs for the VPC configuration of the CodeBuild job |
object({
security_group_ids = list(string)
subnets = list(string)
vpc_id = string
})
| `null` | no | 47 | 48 | ## Outputs 49 | 50 | | Name | Description | 51 | |------|-------------| 52 | | [codebuild\_project\_arn](#output\_codebuild\_project\_arn) | ARN of the CodeBuild Project | 53 | | [codebuild\_project\_name](#output\_codebuild\_project\_name) | Name of the CodeBuild Project | 54 | | [codebuild\_project\_service\_role](#output\_codebuild\_project\_service\_role) | ARN of the CodeBuild Project Service Role | 55 | | [events\_rule\_codebuild\_arn](#output\_events\_rule\_codebuild\_arn) | ARN of the CloudWatch Event Rule for CodeBuild | 56 | | [events\_rule\_pull\_request\_rule\_arn](#output\_events\_rule\_pull\_request\_rule\_arn) | ARN of the CloudWatch Event Rule for Pull Requests | 57 | | [lambda\_function\_arn](#output\_lambda\_function\_arn) | ARN of the Lambda function | 58 | | [lambda\_function\_name](#output\_lambda\_function\_name) | Name of the Lambda function | 59 | | [lambda\_role\_arn](#output\_lambda\_role\_arn) | ARN of the Lambda IAM Role | 60 | | [lambda\_role\_name](#output\_lambda\_role\_name) | Name of the Lambda IAM Role | 61 | 62 | 63 | -------------------------------------------------------------------------------- /modules/review/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.12" 3 | } 4 | 5 | data "aws_partition" "current" {} 6 | 7 | data "aws_caller_identity" "current" {} 8 | 9 | data "aws_region" "current" {} 10 | 11 | locals { 12 | stage = "review" 13 | stage_description = "Execute a job/buildspec when a pull request is created or updated in ${var.repo_name}" 14 | 15 | repo_arn = "arn:${data.aws_partition.current.partition}:codecommit:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:${var.repo_name}" 16 | codebuild_log_stream_arn = "arn:${data.aws_partition.current.partition}:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:/aws/codebuild/${var.repo_name}-review-flow-ci:log-stream:*" 17 | } 18 | 19 | locals { 20 | event_pattern_pull_request = <<-PATTERN 21 | { 22 | "detail-type": ["CodeCommit Pull Request State Change"], 23 | "source": ["aws.codecommit"], 24 | "resources": ["${local.repo_arn}"], 25 | "detail": { 26 | "event": [ 27 | "pullRequestCreated", 28 | "pullRequestSourceBranchUpdated" 29 | ], 30 | "pullRequestStatus": ["Open"], 31 | "isMerged": ["False"] 32 | } 33 | } 34 | PATTERN 35 | } 36 | 37 | locals { 38 | event_pattern_codebuild = <<-PATTERN 39 | { 40 | "detail-type": ["CodeBuild Build State Change"], 41 | "source": ["aws.codebuild"], 42 | "detail": { 43 | "project-name": ["${module.runner.codebuild_project_name}"] 44 | } 45 | } 46 | PATTERN 47 | } 48 | 49 | module "handler" { 50 | source = "../_internal/handler" 51 | 52 | handler = "review_handler" 53 | name_prefix = var.name_prefix 54 | repo_name = var.repo_name 55 | stage = local.stage 56 | stage_description = local.stage_description 57 | project_arn = module.runner.codebuild_project_arn 58 | python_runtime = var.python_runtime 59 | lambda_policy_override = data.aws_iam_policy_document.handler.json 60 | } 61 | 62 | module "runner" { 63 | source = "../_internal/runner" 64 | 65 | name_prefix = var.name_prefix 66 | repo_name = var.repo_name 67 | stage = local.stage 68 | stage_description = local.stage_description 69 | buildspec = var.buildspec 70 | artifacts = var.artifacts 71 | environment = var.environment 72 | environment_variables = var.environment_variables 73 | policy_override = var.policy_override 74 | policy_arns = var.policy_arns 75 | vpc_config = var.vpc_config 76 | 77 | badge_enabled = var.badge_enabled 78 | build_timeout = var.build_timeout 79 | queued_timeout = var.queued_timeout 80 | encryption_key = var.encryption_key 81 | source_version = var.source_version 82 | tags = var.tags 83 | } 84 | 85 | module "trigger_pull_request" { 86 | source = "../_internal/trigger" 87 | 88 | name_prefix = var.name_prefix 89 | repo_name = var.repo_name 90 | stage = "${local.stage}-pull-request" 91 | stage_description = local.stage_description 92 | target_arn = module.handler.function_arn 93 | event_pattern = local.event_pattern_pull_request 94 | } 95 | 96 | module "trigger_codebuild" { 97 | source = "../_internal/trigger" 98 | 99 | name_prefix = var.name_prefix 100 | repo_name = var.repo_name 101 | stage = "${local.stage}-codebuild" 102 | stage_description = local.stage_description 103 | target_arn = module.handler.function_arn 104 | event_pattern = local.event_pattern_codebuild 105 | } 106 | 107 | resource "aws_lambda_permission" "trigger_pull_request" { 108 | action = "lambda:InvokeFunction" 109 | function_name = module.handler.function_name 110 | principal = "events.amazonaws.com" 111 | source_arn = module.trigger_pull_request.events_rule_arn 112 | } 113 | 114 | resource "aws_lambda_permission" "trigger_codebuild" { 115 | action = "lambda:InvokeFunction" 116 | function_name = module.handler.function_name 117 | principal = "events.amazonaws.com" 118 | source_arn = module.trigger_codebuild.events_rule_arn 119 | } 120 | 121 | # IAM policy documents 122 | 123 | data "aws_iam_policy_document" "handler" { 124 | statement { 125 | actions = ["codecommit:PostCommentForPullRequest"] 126 | resources = [local.repo_arn] 127 | } 128 | 129 | statement { 130 | actions = ["logs:GetLogEvents"] 131 | resources = [local.codebuild_log_stream_arn] 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /modules/review/outputs.tf: -------------------------------------------------------------------------------- 1 | output "codebuild_project_arn" { 2 | description = "ARN of the CodeBuild Project" 3 | value = module.runner.codebuild_project_arn 4 | } 5 | 6 | output "codebuild_project_name" { 7 | description = "Name of the CodeBuild Project" 8 | value = module.runner.codebuild_project_name 9 | } 10 | 11 | output "codebuild_project_service_role" { 12 | description = "ARN of the CodeBuild Project Service Role" 13 | value = module.runner.codebuild_project_service_role 14 | } 15 | 16 | output "events_rule_codebuild_arn" { 17 | description = "ARN of the CloudWatch Event Rule for CodeBuild" 18 | value = module.trigger_codebuild.events_rule_arn 19 | } 20 | 21 | output "events_rule_pull_request_rule_arn" { 22 | description = "ARN of the CloudWatch Event Rule for Pull Requests" 23 | value = module.trigger_pull_request.events_rule_arn 24 | } 25 | 26 | output "lambda_function_arn" { 27 | description = "ARN of the Lambda function" 28 | value = module.handler.function_arn 29 | } 30 | 31 | output "lambda_function_name" { 32 | description = "Name of the Lambda function" 33 | value = module.handler.function_name 34 | } 35 | 36 | output "lambda_role_arn" { 37 | description = "ARN of the Lambda IAM Role" 38 | value = module.handler.role_arn 39 | } 40 | 41 | output "lambda_role_name" { 42 | description = "Name of the Lambda IAM Role" 43 | value = module.handler.function_arn 44 | } 45 | -------------------------------------------------------------------------------- /modules/review/variables.tf: -------------------------------------------------------------------------------- 1 | variable "repo_name" { 2 | type = string 3 | description = "Name of the CodeCommit repository" 4 | } 5 | 6 | variable "name_prefix" { 7 | type = string 8 | description = "Prefix to attach to repo name" 9 | default = "" 10 | nullable = false 11 | } 12 | 13 | variable "buildspec" { 14 | type = string 15 | description = "Buildspec used when a pull request is created or updated" 16 | default = "" 17 | } 18 | 19 | variable "badge_enabled" { 20 | type = bool 21 | description = "Generates a publicly-accessible URL for the projects build badge" 22 | default = null 23 | } 24 | 25 | variable "build_timeout" { 26 | type = number 27 | description = "How long in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait until timing out any related build that does not get marked as completed" 28 | default = null 29 | } 30 | 31 | variable "queued_timeout" { 32 | type = number 33 | description = "How long in minutes, from 5 to 480 (8 hours), a build is allowed to be queued before it times out" 34 | default = null 35 | } 36 | 37 | variable "encryption_key" { 38 | type = string 39 | description = "The AWS Key Management Service (AWS KMS) customer master key (CMK) to be used for encrypting the build project's build output artifacts" 40 | default = null 41 | } 42 | 43 | variable "source_version" { 44 | type = string 45 | description = "A version of the build input to be built for this project. If not specified, the latest version is used" 46 | default = null 47 | } 48 | 49 | variable "tags" { 50 | type = map(string) 51 | description = "A map of tags to assign to the resource" 52 | default = {} 53 | } 54 | 55 | variable "artifacts" { 56 | type = map(string) 57 | description = "Map defining an artifacts object for the CodeBuild job" 58 | default = {} 59 | } 60 | 61 | variable "environment" { 62 | type = map(string) 63 | description = "Map describing the environment object for the CodeBuild job" 64 | default = {} 65 | } 66 | 67 | variable "environment_variables" { 68 | type = list(map(string)) 69 | description = "List of environment variable map objects for the CodeBuild job" 70 | default = [] 71 | } 72 | 73 | variable "vpc_config" { 74 | description = "Object of inputs for the VPC configuration of the CodeBuild job" 75 | type = object({ 76 | security_group_ids = list(string) 77 | subnets = list(string) 78 | vpc_id = string 79 | }) 80 | default = null 81 | } 82 | 83 | variable "policy_arns" { 84 | type = list(string) 85 | description = "List of IAM policy ARNs to attach to the CodeBuild service role or null to support ignoring externally attached policies" 86 | default = [] 87 | } 88 | 89 | variable "policy_override" { 90 | type = string 91 | description = "IAM policy document in JSON that extends the basic inline CodeBuild service role" 92 | default = "" 93 | } 94 | 95 | variable "python_runtime" { 96 | type = string 97 | description = "Python runtime for the handler Lambda function" 98 | default = null 99 | } 100 | -------------------------------------------------------------------------------- /modules/schedule/README.md: -------------------------------------------------------------------------------- 1 | # terraform-aws-codecommit-flow-ci/schedule 2 | 3 | Trigger a build on a schedule. 4 | 5 | 6 | ## Requirements 7 | 8 | | Name | Version | 9 | |------|---------| 10 | | [terraform](#requirement\_terraform) | >= 0.12 | 11 | 12 | ## Providers 13 | 14 | | Name | Version | 15 | |------|---------| 16 | | [aws](#provider\_aws) | n/a | 17 | 18 | ## Resources 19 | 20 | | Name | Type | 21 | |------|------| 22 | 23 | ## Inputs 24 | 25 | | Name | Description | Type | Default | Required | 26 | |------|-------------|------|---------|:--------:| 27 | | [repo\_name](#input\_repo\_name) | Name of the CodeCommit repository | `string` | n/a | yes | 28 | | [schedule\_expression](#input\_schedule\_expression) | CloudWatch Event schedule that triggers the CodeBuild job | `string` | n/a | yes | 29 | | [artifacts](#input\_artifacts) | Map defining an artifacts object for the CodeBuild job | `map(string)` | `{}` | no | 30 | | [badge\_enabled](#input\_badge\_enabled) | Generates a publicly-accessible URL for the projects build badge | `bool` | `null` | no | 31 | | [build\_timeout](#input\_build\_timeout) | How long in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait until timing out any related build that does not get marked as completed | `number` | `null` | no | 32 | | [buildspec](#input\_buildspec) | Buildspec used when a tag reference is created or updated | `string` | `""` | no | 33 | | [encryption\_key](#input\_encryption\_key) | The AWS Key Management Service (AWS KMS) customer master key (CMK) to be used for encrypting the build project's build output artifacts | `string` | `null` | no | 34 | | [environment](#input\_environment) | Map describing the environment object for the CodeBuild job | `map(string)` | `{}` | no | 35 | | [environment\_variables](#input\_environment\_variables) | List of environment variable map objects for the CodeBuild job | `list(map(string))` | `[]` | no | 36 | | [name\_prefix](#input\_name\_prefix) | Prefix to attach to repo name | `string` | `""` | no | 37 | | [policy\_arns](#input\_policy\_arns) | List of IAM policy ARNs to attach to the CodeBuild service role or null to support ignoring externally attached policies | `list(string)` | `[]` | no | 38 | | [policy\_override](#input\_policy\_override) | IAM policy document in JSON that extends the basic inline CodeBuild service role | `string` | `""` | no | 39 | | [python\_runtime](#input\_python\_runtime) | Python runtime for the handler Lambda function | `string` | `null` | no | 40 | | [queued\_timeout](#input\_queued\_timeout) | How long in minutes, from 5 to 480 (8 hours), a build is allowed to be queued before it times out | `number` | `null` | no | 41 | | [source\_version](#input\_source\_version) | A version of the build input to be built for this project. If not specified, the latest version is used | `string` | `null` | no | 42 | | [tags](#input\_tags) | A map of tags to assign to the resource | `map(string)` | `{}` | no | 43 | | [vpc\_config](#input\_vpc\_config) | Object of inputs for the VPC configuration of the CodeBuild job |
object({
security_group_ids = list(string)
subnets = list(string)
vpc_id = string
})
| `null` | no | 44 | 45 | ## Outputs 46 | 47 | | Name | Description | 48 | |------|-------------| 49 | | [codebuild\_project\_arn](#output\_codebuild\_project\_arn) | ARN of the CodeBuild Project | 50 | | [codebuild\_project\_name](#output\_codebuild\_project\_name) | Name of the CodeBuild Project | 51 | | [codebuild\_project\_service\_role](#output\_codebuild\_project\_service\_role) | ARN of the CodeBuild Project Service Role | 52 | | [events\_rule\_codebuild\_arn](#output\_events\_rule\_codebuild\_arn) | ARN of the CloudWatch Event Rule for CodeBuild | 53 | | [lambda\_function\_arn](#output\_lambda\_function\_arn) | ARN of the Lambda function | 54 | | [lambda\_function\_name](#output\_lambda\_function\_name) | Name of the Lambda function | 55 | | [lambda\_role\_arn](#output\_lambda\_role\_arn) | ARN of the Lambda IAM Role | 56 | | [lambda\_role\_name](#output\_lambda\_role\_name) | Name of the Lambda IAM Role | 57 | 58 | 59 | -------------------------------------------------------------------------------- /modules/schedule/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.12" 3 | } 4 | 5 | locals { 6 | stage = "schedule" 7 | stage_description = "Execute a job/buildspec on a schedule" 8 | } 9 | 10 | module "handler" { 11 | source = "../_internal/handler" 12 | 13 | handler = "schedule_handler" 14 | name_prefix = var.name_prefix 15 | repo_name = var.repo_name 16 | stage = local.stage 17 | stage_description = local.stage_description 18 | project_arn = module.runner.codebuild_project_arn 19 | python_runtime = var.python_runtime 20 | } 21 | 22 | module "runner" { 23 | source = "../_internal/runner" 24 | 25 | name_prefix = var.name_prefix 26 | repo_name = var.repo_name 27 | stage = local.stage 28 | stage_description = local.stage_description 29 | buildspec = var.buildspec 30 | artifacts = var.artifacts 31 | environment = var.environment 32 | environment_variables = var.environment_variables 33 | policy_override = var.policy_override 34 | policy_arns = var.policy_arns 35 | vpc_config = var.vpc_config 36 | 37 | badge_enabled = var.badge_enabled 38 | build_timeout = var.build_timeout 39 | queued_timeout = var.queued_timeout 40 | encryption_key = var.encryption_key 41 | source_version = var.source_version 42 | tags = var.tags 43 | } 44 | 45 | module "trigger" { 46 | source = "../_internal/trigger" 47 | 48 | name_prefix = var.name_prefix 49 | repo_name = var.repo_name 50 | stage = local.stage 51 | stage_description = local.stage_description 52 | target_arn = module.handler.function_arn 53 | schedule_expression = var.schedule_expression 54 | } 55 | 56 | resource "aws_lambda_permission" "trigger" { 57 | action = "lambda:InvokeFunction" 58 | function_name = module.handler.function_name 59 | principal = "events.amazonaws.com" 60 | source_arn = module.trigger.events_rule_arn 61 | } 62 | -------------------------------------------------------------------------------- /modules/schedule/outputs.tf: -------------------------------------------------------------------------------- 1 | output "codebuild_project_arn" { 2 | description = "ARN of the CodeBuild Project" 3 | value = module.runner.codebuild_project_arn 4 | } 5 | 6 | output "codebuild_project_name" { 7 | description = "Name of the CodeBuild Project" 8 | value = module.runner.codebuild_project_name 9 | } 10 | 11 | output "codebuild_project_service_role" { 12 | description = "ARN of the CodeBuild Project Service Role" 13 | value = module.runner.codebuild_project_service_role 14 | } 15 | 16 | output "events_rule_codebuild_arn" { 17 | description = "ARN of the CloudWatch Event Rule for CodeBuild" 18 | value = module.trigger.events_rule_arn 19 | } 20 | 21 | output "lambda_function_arn" { 22 | description = "ARN of the Lambda function" 23 | value = module.handler.function_arn 24 | } 25 | 26 | output "lambda_function_name" { 27 | description = "Name of the Lambda function" 28 | value = module.handler.function_name 29 | } 30 | 31 | output "lambda_role_arn" { 32 | description = "ARN of the Lambda IAM Role" 33 | value = module.handler.role_arn 34 | } 35 | 36 | output "lambda_role_name" { 37 | description = "Name of the Lambda IAM Role" 38 | value = module.handler.function_arn 39 | } 40 | -------------------------------------------------------------------------------- /modules/schedule/variables.tf: -------------------------------------------------------------------------------- 1 | variable "repo_name" { 2 | type = string 3 | description = "Name of the CodeCommit repository" 4 | } 5 | 6 | variable "name_prefix" { 7 | type = string 8 | description = "Prefix to attach to repo name" 9 | default = "" 10 | nullable = false 11 | } 12 | 13 | variable "schedule_expression" { 14 | type = string 15 | description = "CloudWatch Event schedule that triggers the CodeBuild job" 16 | } 17 | 18 | variable "buildspec" { 19 | type = string 20 | description = "Buildspec used when a tag reference is created or updated" 21 | default = "" 22 | } 23 | 24 | variable "badge_enabled" { 25 | type = bool 26 | description = "Generates a publicly-accessible URL for the projects build badge" 27 | default = null 28 | } 29 | 30 | variable "build_timeout" { 31 | type = number 32 | description = "How long in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait until timing out any related build that does not get marked as completed" 33 | default = null 34 | } 35 | 36 | variable "queued_timeout" { 37 | type = number 38 | description = "How long in minutes, from 5 to 480 (8 hours), a build is allowed to be queued before it times out" 39 | default = null 40 | } 41 | 42 | variable "encryption_key" { 43 | type = string 44 | description = "The AWS Key Management Service (AWS KMS) customer master key (CMK) to be used for encrypting the build project's build output artifacts" 45 | default = null 46 | } 47 | 48 | variable "source_version" { 49 | type = string 50 | description = "A version of the build input to be built for this project. If not specified, the latest version is used" 51 | default = null 52 | } 53 | 54 | variable "tags" { 55 | type = map(string) 56 | description = "A map of tags to assign to the resource" 57 | default = {} 58 | } 59 | 60 | variable "artifacts" { 61 | type = map(string) 62 | description = "Map defining an artifacts object for the CodeBuild job" 63 | default = {} 64 | } 65 | 66 | variable "environment" { 67 | type = map(string) 68 | description = "Map describing the environment object for the CodeBuild job" 69 | default = {} 70 | } 71 | 72 | variable "environment_variables" { 73 | type = list(map(string)) 74 | description = "List of environment variable map objects for the CodeBuild job" 75 | default = [] 76 | } 77 | 78 | variable "vpc_config" { 79 | description = "Object of inputs for the VPC configuration of the CodeBuild job" 80 | type = object({ 81 | security_group_ids = list(string) 82 | subnets = list(string) 83 | vpc_id = string 84 | }) 85 | default = null 86 | } 87 | 88 | variable "policy_arns" { 89 | type = list(string) 90 | description = "List of IAM policy ARNs to attach to the CodeBuild service role or null to support ignoring externally attached policies" 91 | default = [] 92 | } 93 | 94 | variable "policy_override" { 95 | type = string 96 | description = "IAM policy document in JSON that extends the basic inline CodeBuild service role" 97 | default = "" 98 | } 99 | 100 | variable "python_runtime" { 101 | type = string 102 | description = "Python runtime for the handler Lambda function" 103 | default = null 104 | } 105 | -------------------------------------------------------------------------------- /modules/tag/README.md: -------------------------------------------------------------------------------- 1 | # terraform-aws-codecommit-flow-ci/tag 2 | 3 | Trigger a build when a CodeCommit tag is created. 4 | 5 | 6 | ## Requirements 7 | 8 | | Name | Version | 9 | |------|---------| 10 | | [terraform](#requirement\_terraform) | >= 0.12 | 11 | 12 | ## Providers 13 | 14 | | Name | Version | 15 | |------|---------| 16 | | [aws](#provider\_aws) | n/a | 17 | 18 | ## Resources 19 | 20 | | Name | Type | 21 | |------|------| 22 | 23 | ## Inputs 24 | 25 | | Name | Description | Type | Default | Required | 26 | |------|-------------|------|---------|:--------:| 27 | | [repo\_name](#input\_repo\_name) | Name of the CodeCommit repository | `string` | n/a | yes | 28 | | [artifacts](#input\_artifacts) | Map defining an artifacts object for the CodeBuild job | `map(string)` | `{}` | no | 29 | | [badge\_enabled](#input\_badge\_enabled) | Generates a publicly-accessible URL for the projects build badge | `bool` | `null` | no | 30 | | [build\_timeout](#input\_build\_timeout) | How long in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait until timing out any related build that does not get marked as completed | `number` | `null` | no | 31 | | [buildspec](#input\_buildspec) | Buildspec used when a tag reference is created or updated | `string` | `""` | no | 32 | | [encryption\_key](#input\_encryption\_key) | The AWS Key Management Service (AWS KMS) customer master key (CMK) to be used for encrypting the build project's build output artifacts | `string` | `null` | no | 33 | | [environment](#input\_environment) | Map describing the environment object for the CodeBuild job | `map(string)` | `{}` | no | 34 | | [environment\_variables](#input\_environment\_variables) | List of environment variable map objects for the CodeBuild job | `list(map(string))` | `[]` | no | 35 | | [name\_prefix](#input\_name\_prefix) | Prefix to attach to repo name | `string` | `""` | no | 36 | | [policy\_arns](#input\_policy\_arns) | List of IAM policy ARNs to attach to the CodeBuild service role or null to support ignoring externally attached policies | `list(string)` | `[]` | no | 37 | | [policy\_override](#input\_policy\_override) | IAM policy document in JSON that extends the basic inline CodeBuild service role | `string` | `""` | no | 38 | | [python\_runtime](#input\_python\_runtime) | Python runtime for the handler Lambda function | `string` | `null` | no | 39 | | [queued\_timeout](#input\_queued\_timeout) | How long in minutes, from 5 to 480 (8 hours), a build is allowed to be queued before it times out | `number` | `null` | no | 40 | | [source\_version](#input\_source\_version) | A version of the build input to be built for this project. If not specified, the latest version is used | `string` | `null` | no | 41 | | [tags](#input\_tags) | A map of tags to assign to the resource | `map(string)` | `{}` | no | 42 | | [vpc\_config](#input\_vpc\_config) | Object of inputs for the VPC configuration of the CodeBuild job |
object({
security_group_ids = list(string)
subnets = list(string)
vpc_id = string
})
| `null` | no | 43 | 44 | ## Outputs 45 | 46 | | Name | Description | 47 | |------|-------------| 48 | | [codebuild\_project\_arn](#output\_codebuild\_project\_arn) | ARN of the CodeBuild Project | 49 | | [codebuild\_project\_name](#output\_codebuild\_project\_name) | Name of the CodeBuild Project | 50 | | [codebuild\_project\_service\_role](#output\_codebuild\_project\_service\_role) | ARN of the CodeBuild Project Service Role | 51 | | [events\_rule\_codebuild\_arn](#output\_events\_rule\_codebuild\_arn) | ARN of the CloudWatch Event Rule for CodeBuild | 52 | | [lambda\_function\_arn](#output\_lambda\_function\_arn) | ARN of the Lambda function | 53 | | [lambda\_function\_name](#output\_lambda\_function\_name) | Name of the Lambda function | 54 | | [lambda\_role\_arn](#output\_lambda\_role\_arn) | ARN of the Lambda IAM Role | 55 | | [lambda\_role\_name](#output\_lambda\_role\_name) | Name of the Lambda IAM Role | 56 | 57 | 58 | -------------------------------------------------------------------------------- /modules/tag/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.12" 3 | } 4 | 5 | locals { 6 | stage = "tag" 7 | stage_description = "Execute a job/buildspec when a tag is created in ${var.repo_name}" 8 | 9 | event_pattern = <<-PATTERN 10 | { 11 | "detail-type": ["CodeCommit Repository State Change"], 12 | "source": ["aws.codecommit"], 13 | "detail": { 14 | "event": [ 15 | "referenceCreated", 16 | "referenceUpdated" 17 | ], 18 | "repositoryName": ["${var.repo_name}"], 19 | "referenceType": ["tag"] 20 | } 21 | } 22 | PATTERN 23 | } 24 | 25 | module "handler" { 26 | source = "../_internal/handler" 27 | 28 | handler = "tag_handler" 29 | name_prefix = var.name_prefix 30 | repo_name = var.repo_name 31 | stage = local.stage 32 | stage_description = local.stage_description 33 | project_arn = module.runner.codebuild_project_arn 34 | python_runtime = var.python_runtime 35 | } 36 | 37 | module "runner" { 38 | source = "../_internal/runner" 39 | 40 | name_prefix = var.name_prefix 41 | repo_name = var.repo_name 42 | stage = local.stage 43 | stage_description = local.stage_description 44 | buildspec = var.buildspec 45 | artifacts = var.artifacts 46 | environment = var.environment 47 | environment_variables = var.environment_variables 48 | policy_override = var.policy_override 49 | policy_arns = var.policy_arns 50 | vpc_config = var.vpc_config 51 | 52 | badge_enabled = var.badge_enabled 53 | build_timeout = var.build_timeout 54 | queued_timeout = var.queued_timeout 55 | encryption_key = var.encryption_key 56 | source_version = var.source_version 57 | tags = var.tags 58 | } 59 | 60 | module "trigger" { 61 | source = "../_internal/trigger" 62 | 63 | name_prefix = var.name_prefix 64 | repo_name = var.repo_name 65 | stage = local.stage 66 | stage_description = local.stage_description 67 | target_arn = module.handler.function_arn 68 | event_pattern = local.event_pattern 69 | } 70 | 71 | resource "aws_lambda_permission" "trigger" { 72 | action = "lambda:InvokeFunction" 73 | function_name = module.handler.function_name 74 | principal = "events.amazonaws.com" 75 | source_arn = module.trigger.events_rule_arn 76 | } 77 | -------------------------------------------------------------------------------- /modules/tag/outputs.tf: -------------------------------------------------------------------------------- 1 | output "codebuild_project_arn" { 2 | description = "ARN of the CodeBuild Project" 3 | value = module.runner.codebuild_project_arn 4 | } 5 | 6 | output "codebuild_project_name" { 7 | description = "Name of the CodeBuild Project" 8 | value = module.runner.codebuild_project_name 9 | } 10 | 11 | output "codebuild_project_service_role" { 12 | description = "ARN of the CodeBuild Project Service Role" 13 | value = module.runner.codebuild_project_service_role 14 | } 15 | 16 | output "events_rule_codebuild_arn" { 17 | description = "ARN of the CloudWatch Event Rule for CodeBuild" 18 | value = module.trigger.events_rule_arn 19 | } 20 | 21 | output "lambda_function_arn" { 22 | description = "ARN of the Lambda function" 23 | value = module.handler.function_arn 24 | } 25 | 26 | output "lambda_function_name" { 27 | description = "Name of the Lambda function" 28 | value = module.handler.function_name 29 | } 30 | 31 | output "lambda_role_arn" { 32 | description = "ARN of the Lambda IAM Role" 33 | value = module.handler.role_arn 34 | } 35 | 36 | output "lambda_role_name" { 37 | description = "Name of the Lambda IAM Role" 38 | value = module.handler.function_arn 39 | } 40 | -------------------------------------------------------------------------------- /modules/tag/variables.tf: -------------------------------------------------------------------------------- 1 | variable "repo_name" { 2 | type = string 3 | description = "Name of the CodeCommit repository" 4 | } 5 | 6 | variable "name_prefix" { 7 | type = string 8 | description = "Prefix to attach to repo name" 9 | default = "" 10 | nullable = false 11 | } 12 | 13 | variable "buildspec" { 14 | type = string 15 | description = "Buildspec used when a tag reference is created or updated" 16 | default = "" 17 | } 18 | 19 | variable "badge_enabled" { 20 | type = bool 21 | description = "Generates a publicly-accessible URL for the projects build badge" 22 | default = null 23 | } 24 | 25 | variable "build_timeout" { 26 | type = number 27 | description = "How long in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait until timing out any related build that does not get marked as completed" 28 | default = null 29 | } 30 | 31 | variable "queued_timeout" { 32 | type = number 33 | description = "How long in minutes, from 5 to 480 (8 hours), a build is allowed to be queued before it times out" 34 | default = null 35 | } 36 | 37 | variable "encryption_key" { 38 | type = string 39 | description = "The AWS Key Management Service (AWS KMS) customer master key (CMK) to be used for encrypting the build project's build output artifacts" 40 | default = null 41 | } 42 | 43 | variable "source_version" { 44 | type = string 45 | description = "A version of the build input to be built for this project. If not specified, the latest version is used" 46 | default = null 47 | } 48 | 49 | variable "tags" { 50 | type = map(string) 51 | description = "A map of tags to assign to the resource" 52 | default = {} 53 | } 54 | 55 | variable "artifacts" { 56 | type = map(string) 57 | description = "Map defining an artifacts object for the CodeBuild job" 58 | default = {} 59 | } 60 | 61 | variable "environment" { 62 | type = map(string) 63 | description = "Map describing the environment object for the CodeBuild job" 64 | default = {} 65 | } 66 | 67 | variable "environment_variables" { 68 | type = list(map(string)) 69 | description = "List of environment variable map objects for the CodeBuild job" 70 | default = [] 71 | } 72 | 73 | variable "vpc_config" { 74 | description = "Object of inputs for the VPC configuration of the CodeBuild job" 75 | type = object({ 76 | security_group_ids = list(string) 77 | subnets = list(string) 78 | vpc_id = string 79 | }) 80 | default = null 81 | } 82 | 83 | variable "policy_arns" { 84 | type = list(string) 85 | description = "List of IAM policy ARNs to attach to the CodeBuild service role or null to support ignoring externally attached policies" 86 | default = [] 87 | } 88 | 89 | variable "policy_override" { 90 | type = string 91 | description = "IAM policy document in JSON that extends the basic inline CodeBuild service role" 92 | default = "" 93 | } 94 | 95 | variable "python_runtime" { 96 | type = string 97 | description = "Python runtime for the handler Lambda function" 98 | default = null 99 | } 100 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | output "branch" { 2 | description = "Outputs from the branch module" 3 | value = var.event == "branch" ? module.branch[0] : null 4 | } 5 | 6 | output "review" { 7 | description = "Outputs from the review module" 8 | value = var.event == "review" ? module.review[0] : null 9 | } 10 | 11 | output "schedule" { 12 | description = "Outputs from the schedule module" 13 | value = var.event == "schedule" ? module.schedule[0] : null 14 | } 15 | 16 | output "tag" { 17 | description = "Outputs from the tag module" 18 | value = var.event == "tag" ? module.tag[0] : null 19 | } 20 | -------------------------------------------------------------------------------- /tests/branch/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | repo_name = "test-branch-flow-ci" 3 | 4 | branches = { 5 | master_branch = { 6 | branch = "master" 7 | policy_arns = [] 8 | } 9 | test_empty_list = { 10 | branch = "test/empty" 11 | policy_arns = [] 12 | } 13 | test_null = { 14 | branch = "test/null" 15 | policy_arns = null 16 | } 17 | } 18 | } 19 | 20 | module "test_branch" { 21 | for_each = local.branches 22 | source = "../../modules/branch" 23 | 24 | name_prefix = "tardigrade-" 25 | repo_name = local.repo_name 26 | branch = each.value.branch 27 | policy_arns = each.value.policy_arns 28 | 29 | environment = { 30 | compute_type = "BUILD_GENERAL1_LARGE" 31 | } 32 | 33 | policy_override = <<-OVERRIDE 34 | { 35 | "Version": "2012-10-17", 36 | "Statement": [ 37 | { 38 | "Action": "codecommit:GitPush", 39 | "Condition": { 40 | "StringLikeIfExists": { 41 | "codecommit:References": [ 42 | "refs/tags/*" 43 | ] 44 | } 45 | }, 46 | "Effect": "Allow", 47 | "Resource": "arn:${data.aws_partition.current.partition}:codecommit:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:${local.repo_name}", 48 | "Sid": "" 49 | } 50 | ] 51 | } 52 | OVERRIDE 53 | } 54 | 55 | data "aws_partition" "current" {} 56 | 57 | data "aws_caller_identity" "current" {} 58 | 59 | data "aws_region" "current" {} 60 | -------------------------------------------------------------------------------- /tests/main/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | branch_repo_name = "test-branch-flow-ci" 3 | 4 | branches = { 5 | master_branch = { 6 | branch = "master" 7 | policy_arns = [] 8 | } 9 | test_empty_list = { 10 | branch = "test/empty" 11 | policy_arns = [] 12 | } 13 | test_null = { 14 | branch = "test/null" 15 | policy_arns = null 16 | } 17 | } 18 | } 19 | 20 | module "test_branch" { 21 | for_each = local.branches 22 | source = "../../" 23 | 24 | name_prefix = "tardigrade-main-" 25 | event = "branch" 26 | branch = each.value.branch 27 | repo_name = local.branch_repo_name 28 | policy_arns = each.value.policy_arns 29 | 30 | environment = { 31 | compute_type = "BUILD_GENERAL1_SMALL" 32 | } 33 | 34 | policy_override = <<-OVERRIDE 35 | { 36 | "Version": "2012-10-17", 37 | "Statement": [ 38 | { 39 | "Action": "codecommit:GitPush", 40 | "Condition": { 41 | "StringLikeIfExists": { 42 | "codecommit:References": [ 43 | "refs/tags/*" 44 | ] 45 | } 46 | }, 47 | "Effect": "Allow", 48 | "Resource": "arn:${data.aws_partition.current.partition}:codecommit:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:${local.branch_repo_name}", 49 | "Sid": "" 50 | } 51 | ] 52 | } 53 | OVERRIDE 54 | } 55 | 56 | locals { 57 | reviews = { 58 | review1 = { 59 | repo_name = "test-review1-flow-ci" 60 | policy_arns = [] 61 | } 62 | review2 = { 63 | repo_name = "test-review2-flow-ci" 64 | policy_arns = null 65 | } 66 | } 67 | } 68 | module "test_review" { 69 | for_each = local.reviews 70 | source = "../../" 71 | 72 | name_prefix = "tardigrade-main-" 73 | event = "review" 74 | repo_name = each.value.repo_name 75 | policy_arns = each.value.policy_arns 76 | badge_enabled = true 77 | build_timeout = 20 78 | queued_timeout = 60 79 | tags = { 80 | Test = "True" 81 | } 82 | 83 | environment = { 84 | compute_type = "BUILD_GENERAL1_SMALL" 85 | } 86 | } 87 | 88 | locals { 89 | on_demand = { 90 | on_demand_1 = { 91 | repo_name = "test-on-demand-1-flow-ci" 92 | policy_arns = [] 93 | } 94 | on_demand_2 = { 95 | repo_name = "test-on-demand-2-flow-ci" 96 | policy_arns = null 97 | } 98 | } 99 | } 100 | 101 | module "test_on_demand" { 102 | for_each = local.on_demand 103 | source = "../../" 104 | 105 | name_prefix = "tardigrade-main-" 106 | event = "on-demand" 107 | repo_name = each.value.repo_name 108 | policy_arns = each.value.policy_arns 109 | 110 | environment = { 111 | compute_type = "BUILD_GENERAL1_SMALL" 112 | } 113 | } 114 | 115 | locals { 116 | schedules = { 117 | schedule1 = { 118 | repo_name = "test-schedule1-flow-ci" 119 | policy_arns = [] 120 | } 121 | schedule2 = { 122 | repo_name = "test-schedule2-flow-ci" 123 | policy_arns = null 124 | } 125 | } 126 | } 127 | 128 | module "test_schedule" { 129 | for_each = local.schedules 130 | source = "../../" 131 | 132 | name_prefix = "tardigrade-main-" 133 | event = "schedule" 134 | repo_name = each.value.repo_name 135 | policy_arns = each.value.policy_arns 136 | 137 | schedule_expression = "cron(0 11 ? * MON-FRI *)" 138 | 139 | environment = { 140 | compute_type = "BUILD_GENERAL1_SMALL" 141 | } 142 | } 143 | 144 | locals { 145 | tags = { 146 | tag1 = { 147 | repo_name = "test-tag1-flow-ci" 148 | policy_arns = [] 149 | } 150 | tag2 = { 151 | repo_name = "test-tag2-flow-ci" 152 | policy_arns = null 153 | } 154 | } 155 | } 156 | 157 | module "test_tag" { 158 | for_each = local.tags 159 | source = "../../" 160 | 161 | name_prefix = "tardigrade-main-" 162 | event = "tag" 163 | repo_name = each.value.repo_name 164 | policy_arns = each.value.policy_arns 165 | 166 | environment = { 167 | compute_type = "BUILD_GENERAL1_SMALL" 168 | } 169 | } 170 | 171 | data "aws_partition" "current" {} 172 | 173 | data "aws_caller_identity" "current" {} 174 | 175 | data "aws_region" "current" {} 176 | 177 | output "test_branch" { 178 | value = module.test_branch 179 | } 180 | 181 | output "test_review" { 182 | value = module.test_review 183 | } 184 | 185 | output "test_schedule" { 186 | value = module.test_schedule 187 | } 188 | 189 | output "test_tag" { 190 | value = module.test_tag 191 | } 192 | -------------------------------------------------------------------------------- /tests/main_vpc/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | branch_repo_name = "test-branch-flow-ci" 3 | 4 | branches = { 5 | master_branch = { 6 | branch = "master" 7 | policy_arns = [] 8 | } 9 | } 10 | } 11 | 12 | module "test_branch" { 13 | for_each = local.branches 14 | source = "../../" 15 | 16 | name_prefix = "tardigrade-vpc-" 17 | event = "branch" 18 | branch = each.value.branch 19 | repo_name = local.branch_repo_name 20 | policy_arns = each.value.policy_arns 21 | vpc_config = local.vpc_config 22 | 23 | environment = { 24 | compute_type = "BUILD_GENERAL1_SMALL" 25 | } 26 | 27 | policy_override = <<-OVERRIDE 28 | { 29 | "Version": "2012-10-17", 30 | "Statement": [ 31 | { 32 | "Action": "codecommit:GitPush", 33 | "Condition": { 34 | "StringLikeIfExists": { 35 | "codecommit:References": [ 36 | "refs/tags/*" 37 | ] 38 | } 39 | }, 40 | "Effect": "Allow", 41 | "Resource": "arn:${data.aws_partition.current.partition}:codecommit:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:${local.branch_repo_name}", 42 | "Sid": "" 43 | } 44 | ] 45 | } 46 | OVERRIDE 47 | } 48 | 49 | locals { 50 | reviews = { 51 | review1 = { 52 | repo_name = "test-review1-flow-ci" 53 | policy_arns = [] 54 | } 55 | review2 = { 56 | repo_name = "test-review2-flow-ci" 57 | policy_arns = null 58 | } 59 | } 60 | } 61 | 62 | module "test_review" { 63 | for_each = local.reviews 64 | source = "../../" 65 | 66 | name_prefix = "tardigrade-vpc-" 67 | event = "review" 68 | repo_name = each.value.repo_name 69 | policy_arns = each.value.policy_arns 70 | badge_enabled = true 71 | build_timeout = 20 72 | queued_timeout = 60 73 | vpc_config = local.vpc_config 74 | 75 | tags = { 76 | Test = "True" 77 | } 78 | 79 | environment = { 80 | compute_type = "BUILD_GENERAL1_SMALL" 81 | } 82 | } 83 | 84 | locals { 85 | schedules = { 86 | schedule1 = { 87 | repo_name = "test-schedule1-flow-ci" 88 | policy_arns = [] 89 | } 90 | schedule2 = { 91 | repo_name = "test-schedule2-flow-ci" 92 | policy_arns = null 93 | } 94 | } 95 | } 96 | 97 | module "test_schedule" { 98 | for_each = local.schedules 99 | source = "../../" 100 | 101 | name_prefix = "tardigrade-vpc-" 102 | event = "schedule" 103 | repo_name = each.value.repo_name 104 | policy_arns = each.value.policy_arns 105 | vpc_config = local.vpc_config 106 | 107 | schedule_expression = "cron(0 11 ? * MON-FRI *)" 108 | 109 | environment = { 110 | compute_type = "BUILD_GENERAL1_SMALL" 111 | } 112 | } 113 | 114 | locals { 115 | tags = { 116 | tag1 = { 117 | repo_name = "test-tag1-flow-ci" 118 | policy_arns = [] 119 | } 120 | tag2 = { 121 | repo_name = "test-tag2-flow-ci" 122 | policy_arns = null 123 | } 124 | } 125 | } 126 | 127 | module "test_tag" { 128 | for_each = local.tags 129 | source = "../../" 130 | 131 | name_prefix = "tardigrade-vpc-" 132 | event = "tag" 133 | repo_name = each.value.repo_name 134 | policy_arns = each.value.policy_arns 135 | vpc_config = local.vpc_config 136 | 137 | environment = { 138 | compute_type = "BUILD_GENERAL1_SMALL" 139 | } 140 | } 141 | 142 | locals { 143 | vpc_config = { 144 | vpc_id = aws_vpc.test.id 145 | security_group_ids = [ 146 | aws_security_group.test.id 147 | ] 148 | subnets = [ 149 | for subnet in aws_subnet.test : subnet.id 150 | ] 151 | } 152 | } 153 | 154 | resource "aws_vpc" "test" { 155 | cidr_block = "10.0.0.0/16" 156 | tags = { 157 | Test = "Tardigrade - terraform-aws-code-commit-flow-ci/main_vpc" 158 | } 159 | } 160 | 161 | resource "aws_subnet" "test" { 162 | for_each = toset(["10.0.0.0/24", "10.0.1.0/24"]) 163 | 164 | vpc_id = aws_vpc.test.id 165 | cidr_block = each.value 166 | tags = { 167 | Test = "Tardigrade - terraform-aws-code-commit-flow-ci/main_vpc" 168 | } 169 | } 170 | 171 | resource "aws_security_group" "test" { 172 | name = "allow_vpc_cidr" 173 | description = "Tardigrade - terraform-aws-code-commit-flow-ci/main_vpc" 174 | vpc_id = aws_vpc.test.id 175 | 176 | ingress { 177 | description = "Tardigrade - terraform-aws-code-commit-flow-ci/main_vpc" 178 | from_port = 0 179 | to_port = 0 180 | protocol = "-1" 181 | cidr_blocks = [aws_vpc.test.cidr_block] 182 | } 183 | 184 | egress { 185 | description = "Tardigrade - terraform-aws-code-commit-flow-ci/main_vpc" 186 | from_port = 0 187 | to_port = 0 188 | protocol = "-1" 189 | cidr_blocks = ["0.0.0.0/0"] 190 | ipv6_cidr_blocks = ["::/0"] 191 | } 192 | 193 | tags = { 194 | Test = "Tardigrade - terraform-aws-code-commit-flow-ci/main_vpc" 195 | } 196 | } 197 | 198 | data "aws_partition" "current" {} 199 | 200 | data "aws_caller_identity" "current" {} 201 | 202 | data "aws_region" "current" {} 203 | 204 | output "test_branch" { 205 | value = module.test_branch 206 | } 207 | 208 | output "test_review" { 209 | value = module.test_review 210 | } 211 | 212 | output "test_schedule" { 213 | value = module.test_schedule 214 | } 215 | 216 | output "test_tag" { 217 | value = module.test_tag 218 | } 219 | -------------------------------------------------------------------------------- /tests/review/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | reviews = { 3 | review1 = { 4 | repo_name = "test-review1-flow-ci" 5 | policy_arns = [] 6 | } 7 | review2 = { 8 | repo_name = "test-review2-flow-ci" 9 | policy_arns = null 10 | } 11 | } 12 | } 13 | 14 | module "test_review" { 15 | for_each = local.reviews 16 | source = "../../modules/review" 17 | 18 | name_prefix = "tardigrade-" 19 | repo_name = each.value.repo_name 20 | policy_arns = each.value.policy_arns 21 | 22 | badge_enabled = true 23 | build_timeout = 20 24 | queued_timeout = 60 25 | tags = { 26 | Test = "True" 27 | } 28 | 29 | 30 | environment = { 31 | compute_type = "BUILD_GENERAL1_LARGE" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/schedule/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | schedules = { 3 | schedule1 = { 4 | repo_name = "test-schedule1-flow-ci" 5 | policy_arns = [] 6 | } 7 | schedule2 = { 8 | repo_name = "test-schedule2-flow-ci" 9 | policy_arns = null 10 | } 11 | } 12 | } 13 | 14 | module "test_schedule" { 15 | for_each = local.schedules 16 | source = "../../modules/schedule" 17 | 18 | name_prefix = "tardigrade-" 19 | repo_name = each.value.repo_name 20 | policy_arns = each.value.policy_arns 21 | 22 | schedule_expression = "cron(0 11 ? * MON-FRI *)" 23 | 24 | environment = { 25 | compute_type = "BUILD_GENERAL1_LARGE" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/tag/main.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | tags = { 3 | tag1 = { 4 | repo_name = "test-tag1-flow-ci" 5 | policy_arns = [] 6 | } 7 | tag2 = { 8 | repo_name = "test-tag2-flow-ci" 9 | policy_arns = null 10 | } 11 | } 12 | } 13 | 14 | 15 | module "test_tag" { 16 | for_each = local.tags 17 | source = "../../modules/tag" 18 | 19 | name_prefix = "tardigrade-" 20 | repo_name = each.value.repo_name 21 | policy_arns = each.value.policy_arns 22 | 23 | environment = { 24 | compute_type = "BUILD_GENERAL1_LARGE" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | variable "event" { 2 | type = string 3 | description = "Type of event that will trigger the flow-ci job" 4 | 5 | validation { 6 | condition = contains( 7 | [ 8 | "branch", 9 | "on-demand", 10 | "review", 11 | "schedule", 12 | "tag", 13 | ], 14 | var.event 15 | ) 16 | 17 | error_message = "The event must be one of: branch, on-demand, review, schedule, or tag." 18 | } 19 | } 20 | 21 | variable "repo_name" { 22 | type = string 23 | description = "Name of the CodeCommit repository" 24 | } 25 | 26 | variable "name_prefix" { 27 | type = string 28 | description = "Prefix to attach to repo name" 29 | default = "" 30 | nullable = false 31 | } 32 | 33 | variable "artifacts" { 34 | type = map(string) 35 | description = "Map defining an artifacts object for the CodeBuild job" 36 | default = {} 37 | } 38 | 39 | variable "badge_enabled" { 40 | type = bool 41 | description = "Generates a publicly-accessible URL for the projects build badge" 42 | default = null 43 | } 44 | 45 | variable "branch" { 46 | type = string 47 | description = "Name of the branch where updates will trigger a build. Used only when `event` is \"branch\"" 48 | default = null 49 | } 50 | 51 | variable "build_timeout" { 52 | type = number 53 | description = "How long in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait until timing out any related build that does not get marked as completed" 54 | default = null 55 | } 56 | 57 | variable "buildspec" { 58 | type = string 59 | description = "Buildspec used when the specified branch is updated" 60 | default = "" 61 | } 62 | 63 | variable "encryption_key" { 64 | type = string 65 | description = "The AWS Key Management Service (AWS KMS) customer master key (CMK) to be used for encrypting the build project's build output artifacts" 66 | default = null 67 | } 68 | 69 | variable "environment" { 70 | type = map(string) 71 | description = "Map describing the environment object for the CodeBuild job" 72 | default = {} 73 | } 74 | 75 | variable "environment_variables" { 76 | type = list(map(string)) 77 | description = "List of environment variable map objects for the CodeBuild job" 78 | default = [] 79 | } 80 | 81 | 82 | variable "policy_arns" { 83 | type = list(string) 84 | description = "List of IAM policy ARNs to attach to the CodeBuild service role" 85 | default = [] 86 | } 87 | 88 | variable "policy_override" { 89 | type = string 90 | description = "IAM policy document in JSON that extends the basic inline CodeBuild service role" 91 | default = "" 92 | } 93 | 94 | variable "python_runtime" { 95 | type = string 96 | description = "Python runtime for the handler Lambda function" 97 | default = null 98 | } 99 | 100 | variable "queued_timeout" { 101 | type = number 102 | description = "How long in minutes, from 5 to 480 (8 hours), a build is allowed to be queued before it times out" 103 | default = null 104 | } 105 | 106 | variable "schedule_expression" { 107 | type = string 108 | description = "CloudWatch Event schedule that triggers the CodeBuild job. Required when `event` is \"schedule\"" 109 | default = null 110 | } 111 | 112 | variable "source_version" { 113 | type = string 114 | description = "A version of the build input to be built for this project. If not specified, the latest version is used" 115 | default = null 116 | } 117 | 118 | variable "tags" { 119 | type = map(string) 120 | description = "A map of tags to assign to the resource" 121 | default = {} 122 | } 123 | 124 | variable "vpc_config" { 125 | description = "Object of inputs for the VPC configuration of the CodeBuild job" 126 | type = object({ 127 | security_group_ids = list(string) 128 | subnets = list(string) 129 | vpc_id = string 130 | }) 131 | default = null 132 | } 133 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.13" 3 | 4 | required_providers { 5 | aws = { 6 | source = "hashicorp/aws" 7 | version = ">= 3.28.0" 8 | } 9 | } 10 | } 11 | --------------------------------------------------------------------------------